Properly append to an array when handling presses - javascript

Within my function, through interaction from the user, I aim slowly build up an array of responses which I then pass off to an API. However, different approaches to append to the array, simply return a single position array (overwrite).
My current code as follows:
const contribution: Array = [];
const handlePress = () => {
var col = {
response,
user: 1,
update: update.id,
question: q.id,
};
contribution = [...contribution, col];
}
My understanding is that contribution = [...contribution, col] is the correct way to add to the array.
What is the best practice approach for doing this inside a function called each time the user interacts?

Although it is not clear from the question, I suspect, this code is inside a component. If so, then a new contribution array is created on every render. You need to use useState to store this array so that a new array is not created on every render.
const [contribution, setContribution] = React.useState([]);
const handlePress = () => {
var col = {
response,
user: 1,
update: update.id,
question: q.id,
};
setContribution([...contribution, col]);
}

Related

React State is Changing Without Using setState()

I'm developing a quiz/test website. I want to see different questions when I move to next question and don't want to see same answers at the same time.
I have a state array varaible which is calling allWords. This state
will keep all words.
And I have another state array variable which calls like
neverAskedWords. This state will keep words which never used
always.
I'm creating a new array variable and defining with allWords in a function. When I'm removing any record in the new array variable then that record is removing in allWords variable as well... Why?
I want to remove any record in that temporary array and want to save updated version to neverAskedWords state. In this way I could see different questions always. Here is my codes.
const [allWords, setAllWords] = useState([])
const [neverAskedWords, setNeverAskedWords] = useState([])
async function getAllData(){
axios
.get(`http://127.0.0.1:3000/api/improve-language`)
.then(res => {
setAllWords(res.data)//defining allWords
setNeverAskedWords(res.data)//defining neverAskedWords
firstQuestionAndAnswers(res.data)//sending all datas by parameter, bacause when I'm trying to get datas by using `allWords` state, it would be undefined. That's why sending all data by parameter for the first time to set first question and answers.
})
.catch(err =>{
console.log(err)
})
}
async function firstQuestionAndAnswers(wordsList){
let neverAskedList = await wordsList //creating and defining temporary variables
const allWordsList = await wordsList //creating and defining temporary variables
//some not necessary codes for this issue
const questionIndex = randomNumber(neverAskedList.length)
const firstQuestion = neverAskedList[questionIndex]
let firstAnswers = []
for (let i = 0; i < 4; i++) {
let answerIndex = randomNumber(allWordsList.length)
firstAnswers[i] = allWordsList[answerIndex]
allWordsList.splice(answerIndex, 1)//and here! I'm removing this record to prevent using it again next time, there will be different answers always
}
//some not necessary codes for this issue
firstAnswers.push(firstQuestion)
const randomisedAnswers = firstAnswers.sort(()=>Math.random() - 0.5)
//some not necessary codes for this issue
setQuestion(firstQuestion)
setAnswers(randomisedAnswers)
//and then here! I'm removing the used question in this time to prevent using it again, there will be different questions always and never see this question again
neverAskedList.splice(questionIndex, 1)
setNeverAskedWords(neverAskedList)
}
allWords should'nt change. But changing, because of why?
So the most obvious thing that I see in your code is that you are modifying the same object. What you should do instead is use the spread operator.
const [allWords, setAllWords] = useState([])
const [neverAskedWords, setNeverAskedWords] = useState([])
async function getAllData(){
axios
.get(`http://127.0.0.1:3000/api/improve-language`)
.then(res => {
setAllWords(res.data)//defining allWords
setNeverAskedWords(res.data)//defining neverAskedWords
firstQuestionAndAnswers(res.data)//sending all datas by parameter, bacause when I'm trying to get datas by using `allWords` state, it would be undefined. That's why sending all data by parameter for the first time to set first question and answers.
})
.catch(err =>{
console.log(err)
})
}
async function firstQuestionAndAnswers(wordsList){
// don't use await for js objects, should be used only with promises.
// use spread operator to make copy of the wordList array so you never actually modify the original object
let neverAskedList = [...wordsList]
const allWordsList = [...wordsList]
//some not necessary codes for this issue
const questionIndex = randomNumber(neverAskedList.length)
const firstQuestion = neverAskedList[questionIndex]
let firstAnswers = []
for (let i = 0; i < 4; i++) {
let answerIndex = randomNumber(allWordsList.length)
firstAnswers[i] = allWordsList[answerIndex]
allWordsList.splice(answerIndex, 1)//and here! I'm removing this record to prevent using it again next time, there will be different answers always
}
//some not necessary codes for this issue
firstAnswers.push(firstQuestion)
const randomisedAnswers = firstAnswers.sort(()=>Math.random() - 0.5)
//some not necessary codes for this issue
setQuestion(firstQuestion)
setAnswers(randomisedAnswers)
//and then here! I'm removing the used question in this time to prevent using it again, there will be different questions always and never see this question again
neverAskedList.splice(questionIndex, 1)
setNeverAskedWords(neverAskedList)
}
If you don't understand why it happened then here's a short explanation. In js when you do const a = { key: 'val' } you created a variable that references the memory block that is actually storing your object. And when you do const b = a you are creating another variable that references the same memory block. So updating 1 automatically changes the other one.

Adding new object in array in existing array

I have a CRUD functionality in my react application using hooks and es6.
I'm using AntDesign List Component as well. In my add function, after adding for the first time, it's expected that it's been added on the list.
But after creating another one, it's just replacing the existing array. It's not been added like index 1. It's always index 0.
Here's my code
const leaveTypeList = [];
const [isLeaveType, setLeaveType] = useState(leaveTypeList);
useEffect(() => {
setLeaveType(leaveTypeList);
}, [setLeaveType]);
const payload = {
leaveTag: values.leaveTag,
leaveTagColor: values.leaveTagColor,
hexCode: state.tagColor,
dateCreated: formatLeaveTypeDate,
};
leaveTypeList.push(payload);
setLeaveType(leaveTypeList);
hey since you need to add a new item to array there is no need of the useEffect hook
const leaveTypeList = [];
const [isLeaveType, setLeaveType] = useState(leaveTypeList);
const payload = {
leaveTag: values.leaveTag,
leaveTagColor: values.leaveTagColor,
hexCode: state.tagColor,
dateCreated: formatLeaveTypeDate,
};
setLeaveType([...isLeaveType,payload]);
you can simple use setter to set new state updating the existing array by adding the new payload to it.
You should push the new data by isLeaveType.push(payload).
Also,
useEffect(() => {
setLeaveType(leaveTypeList);
}, [setLeaveType]);
makes no sense to me.
Since the setLeaveType is the setter for isLeaveType, there is no reason for the effect to depend on it.

How to create a `context.Provider`/`context.Consumer`-like structure to pass values in a bot app?

I'm trying to pass a property, that is inside the first position of an array of objects, to another module so I can use this value later. I've tried to pass it as module(args), but it keeps reading the default value which is 0. Is there a way to do this?
I tried to implement some React.context but the Bot framework Emulator is refusing it.
/////////////////Module that ll acquire the value/////////////////////////////
getCard(bot, builder, params) {
let configValues = { ...params[0] }
bot.dialog(`${configValues.path}`, function (session) {
var msg = new builder.Message(session);
const cardItem = (obj) => {
return (new builder.HeroCard(session)
.title(`${obj.title}`)
.text(`R$ ${obj.price}`)
.images([builder.CardImage.create(session, `${obj.img}`)])
.buttons([
builder.CardAction.imBack(session, `${obj.price} Item adicionado!`, 'add to cart')
// !onClick event must add the current obj.price to
// the configValues.total(Ex: configValues.total += obj.price)!
])
)
}
msg.attachmentLayout(builder.AttachmentLayout.carousel)
msg.attachments(
eval(params.map(obj => cardItem(obj)))
);
//!in here before end the dialog is where i want to update
// the configValues.total so i can show it in the -> Checkout module
session.send(msg).endDialog()
}).triggerAction({ matches: configValues.regex });
}
}
//////////////CheckOut.Module///////////////////////////////
{...}
let configValues = { ...params[0] }
let state = {
nome: "",
endereco: "",
pagamento: "",
total: configValues.total // this is the value to be read
}
bot.dialog('/intent', [
{...},
(session, results) => {
state.pagamento = results.response
session.send(
JSON.stringify(state) // here is the place to be printed
)
{...}
]
).triggerAction({ matches: /^(finalizar|checar|encerrar|confirmar pedido|terminar)/i })
Since you solved your original problem, I'll answer the one in your comment.
Your problem is here:
cartId.map((obj, i , arr) => {
// if (!obj.total) {
// obj.total.reduce(i => i += i)
// }
const newtotal = new total
newtotal.getTotals(bot, builder, obj, arr)
})
cartId contains the totals for each of your items. When you call map on it, you're passing each item individually to getTotals, which passes each item to checkout()
The reason you can't sum all of the totals and can only sum one item's total is that you pass cartId to checkout and cartId has been changed to just a single item. Instead, there's a couple of different things you could do:
Pass the whole cartId from cartItems and use something like for (var key in cartItems) in totalConstructor() and checkoutConstructor(). This is probably the easiest, but not very memory efficient.
Use BotBuilder's State Storage to store your totals array in userData, then sum that at the end. This might be more difficult to implement, but would be a much better route to go. Here's a sample that can help you get started.

Pass value from map to state

//It's working now - updated code
I'm working on my own autocomplete component because I have problem with passing firebase data to a ready one.
The whole mechanism is working good but I have problem with passing values after getting user input
I'm setting initial state with those values
const INITIAL_STATE = {
allChars: [],
suggestions: [],
value: ""
};
Then in autocomplete class i'm loading all users from database
loadData(){
let self = this;
let characters = firebase.firestore().collection("users");
characters.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
let document = doc.data();
self.setState(({allChars})=>({
allChars: [
...allChars,
document
]
}))
});
});
}
Here is my getSuggestions function. It is firing on input change
getSuggestions = event => {
const {value, suggestions} = event.target;
this.setState({
value: value,
suggestions: []
})
let suggest = [];
this.state.allChars.map((allChars) => {
if(value.length > 1 && allChars.name.toLowerCase().includes(value.toLowerCase())){
suggest.push (
allChars.name
);
}
})
this.setState({
suggestions: suggest
})
}
In render I just put {sugestions}
But in {suggestions} I get rendered only one name.
one
But when I console.log it - I get two names
two
There should be two.
I tried to set state in this function like in loadData(), but I still get only one value.
Is there other way to get both values into DOM
Full code can be found here: https://github.com/Ilierette/react-planner/blob/master/src/component/elements/Autocomplete.js
I think the reason you are just seeing one element each time your components re-render is that in your map function on your allChars array, when you want to update the suggestions in your state, you are setting just the name each time as a new array while you should update the existing array in your state, so your code should be:
this.setState({
suggestions: [...this.state.suggestions, allChars.name]
})

Update specific object in array in state

I'm trying to update an object in an array which is in my state.
I have an array of objects 'this.state.webs' which is presented in multiple div`s on a page. Each one has an onclick method which send the object to a function, then I do an API call and returns a set of 'sub webs' which I want to add to the object in the property 'subs'.
My state:
this.state = {
webs: this.props.webs
}
My template:
<Nav
groups={[
{
links: this.state.webs
}
]}
expandedStateText={'expanded'}
collapsedStateText={'collapsed'}
selectedKey={'key3'}
onLinkClick={this._openWeb.bind(this)}
/>
Onclick function:
private async _openWeb(r, n): Promise<void> {
const service = new MyService();
var subs = await service.getSubs(n);
n.subs = subs;
### How do I update 'n' with the subs? setState({ ? })
}
So, when a user clicks a web, I am fetching some sub webs and then I want to update the parent object n with the children (subs).
You can update your webs array with everything before n, a clone of n with the updated subs, and everything after n:
private async _openWeb(r, n): Promise<void> {
const service = new MyService();
const subs = await service.getSubs(n);
const { webs } = this.state;
const nIndex = webs.indexOf(n);
this.setState({
webs: [
...webs.slice(0, nIndex),
{ ...n, subs },
...webs.slice(nIndex + 1)
]
})
}
Based on the answer from Tholle and Filip W, I came up with my own approach.
Gladly to recieve some comments if this approach isn't recommended, but I find it easier to understand than the suggestion from Tholle.
const webs = [...this.state.webs];
const index = webs.indexOf(n);
webs[index].links = links;
webs[index].isExpanded = true;
this.setState({webs})
From my experiences with presenting array of data it was useful to also send an index of the targeted object (or div where click happened).
So something like this could be useful:
const webs = this.state.webs
webs[index].subs = subs
this.setState({webs})

Categories

Resources