First i set ids as an object
attribute.map((item, index) => {
setIds({ ...ids, [index]: item.id })
})
then i try to push it in an array, but it is only pushing last object
let idsArr = []
Object.keys(ids).map(key => {
idsArr.push(ids[key])
})
one proposal can be to push the map result in array
idsArr.push(...Object.keys(ids).map(key => ids[key]));
Moreover when you set the id if you don't return nothing the map method should not be used.
A loop like foreach is a better choice
map method is used to get a "transformed" array
attribute.forEach((item, index) => {
setIds({ ...ids, [index]: item.id })
})
let idsArr = [];
let ids = {
'test': 1,
'test2': 2
};
idsArr.push(...Object.keys(ids).map(key => ids[key]));
console.log(idsArr);
Issue
You are enqueueing a bunch of React state updates inside a loop, using a normal update, and as each update is processed by React it uses the same value of ids from the render cycle they were all enqueued in. In other words, each update stomps the previous one and the last state update wins. This is the state value you see on the subsequent render cycle.
Solutions
Use functional state updates to enqueue each update and have each update from the previous state instead of the state from the callback closure.
Example:
attribute.forEach((item, index) => {
setIds(ids => ({
...ids,
[index]: item.id,
}))
});
With the correctly updated ids array state, you should be able to iterate and push values into the other array:
const idsArr = [];
Object.keys(ids).map(key => {
idsArr.push(ids[key]);
});
Or just get the array of values directly:
const idsArr = Object.values(ids);
Related
I got a problem with this Function. When I trigger this function it only re render the component the first trigger. After that not any more. I cant find the problem :(
function selectAnswer(id, questId) {
let newArr = questions
for(let i = 0; i < newArr.length; i++){
if(questId === newArr[i].id){
const changedAnswers = newArr[i].answers.map(answer => {
return answer.id === id ?
{...answer, selected: !answer.selected} :
{...answer, selected: false}
})
newArr.forEach(element => {
if(questId === element.id){
element.answers = changedAnswers
}
})
}
}
setQuestions(newArr)
}
You're never actually updating the state. This doesn't create a copy of the array, it just duplicates a reference to the array:
let newArr = questions
So this isn't setting state to a new array, but just a reference to the same array:
setQuestions(newArr)
Additionally, instead of creating new state, you are mutating the existing state:
element.answers = changedAnswers
Start by creating a new array, even if it contains the same elements as the original:
let newArr = [...questions];
Then, when you want to modify one of those elements, instead of modifying the existing element you would instead replace it with a new one. So instead of this structure:
newArr.forEach(element => {
});
You could instead replace your new array with a .map() over itself:
newArr = newArr.map(element => {
});
And within that .map() you would return either the unmodified object or the replacement object:
newArr = newArr.map(element => {
if(questId === element.id) {
return {...element, answers: changedAnswers};
} else {
return element;
}
});
Overall, the idea here is that you don't want to loop over an existing array, modify its values, and set that array back to state. That's not "updating state" in the React sense but instead it's "mutating state". Instead, create a new array and populate it with the objects you want. Those objects can be a mix of unchanged objects from the existing array and replaced (not modified) objects from the existing array.
Don't really know why it is saying this but this is the code I believe it is talking about
setUpdate(text, key) {
const items = this.state.items;
items.map((item) => {
if (item.key === key) {
item.text = text;
}
});
this.setState({
items: items,
});
}
Issue
The Array.prototype.map function maps a one-to-one relationship, the resultant array will be the same length as the original. The error is saying that you aren't explicitly returning a value for each element iterated.
You also don't save the resultant mapped array. The result of your code is iteration over the items array and mutation with item.text = text;. In React state and props are to be considered immutable, mutating them directly is very anti-pattern.
setUpdate(text, key){
const items = this.state.items; // <-- saved state reference
items.map(item=>{ // <-- missing result array save
if(item.key===key){
item.text= text; // <-- state mutation!
}
// <-- missing return value (the error)
})
this.setState({
items: items // <-- same reference to previous state!
})
}
Solution
You will want to shallow copy the array as well as any nested object that are being updated. Use a functional state update to correctly create the next state array from the state array from the previous state. The reason for this is that the next version of the items necessarily depends on the previous array.
setUpdate(text, key) {
this.setState((prevState) => ({
items: prevState.items.map((item) =>
item.key === key ? { ...item, key: text } : item
)
}));
}
You have to use like below
setUpdate(text, key) {
// loop the existing state and find the key value
const nextItem = this.state.items.map((item) => {
if (item.key === key) {
item.text = text;
}
// return the modified item
return item;
});
// set the new object into state
this.setState({items: nextItem });
}
Wrote this answer without realising that React requires you to provide a new array.
You shouldn't be using map just to find and update an item in an array! What's wrong with find?
find will stop on the first element that matches the condition.
setUpdate(text, key){
const items = this.state.items;
const item = items.find(i => i.key === key);
if (item) item.text = text;
this.setState({
items: items
});
}
The error states that add a return keywork inside of map arrow function not just any HTML or compnent
Eg `
{props.meetups.map((meetup) => {
return (
<MeetupItem
key={meetup.id}
id={meetup.id}
image={meetup.image}
title={meetup.title}
address={meetup.address}
description={meetup.description}
/>
);
})}
</ul>
`
I am creating a dropdown filter to update the search results of a page using react hooks. Basically, I am passing an array with the options that the user chose from the dropdown menu. I am successfully updating the global state with the new arrays BUT my issue is useState creates a NEW array instead of merging the results with the previous state.
Above you can see, I made two calls with different filter options and the global state now holds 2 arrays. My goal is to have both arrays merged into one.
This is the function where the global state is being updated.
const Results = () => {
const [filterList, setFilterList] = useState([])
const setGlobalFilter = (newFilter) => {
let indexFilter = filterList.indexOf(newFilter);
// console.log("Top level index", indexFilter)
indexFilter ?
setFilterList([...new Set([...filterList, newFilter])]) :
setFilterList(filterList => filterList.filter((filter, i) => i !== indexFilter))
}
// console.log("TopFilterSelection:", filterList)
return (
<div>
<Filter setFilter={(filterList) => setGlobalFilter(filterList)}/>
</div>
)
}
I've been checking on using prevState like this:
...
setFilterList(prevState => [...new Set([...prevState, newFilter])]) :
...
But I don't know what I am doing wrong.
Any feedback would be much appreciated!
This happens because newFilteris an array, not a word.
Should be
setFilterList(previous => [...new Set([...previous, ...newFilter])])
Also this
let indexFilter = filterList.indexOf(newFilter);
always returns -1 if newFilteris an array (since you a sending brand new array each time), it's not a falsy value, be careful
Use the .concat method.
setFilterList(filterList.concat(newFilter))
Read more about it here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
I have a array with values that I use a forEach to pass values as props to a component. I also want to send a value as props, and increase it by 1 for every iteration. Currently I have tried:
this.state = {
index: 0
}
let news:any[] = []; //container for other items for the loop
let indx = this.state.index;
this.state.listItems.forEach((myArrWItems) => {
indx = +1;
news.push(<MyComponent
index = {indx}
/>);
});
However, the value sent in as props is always 1. Any help?
Your index state variable is not necessary there. Since you want to create an array from an existing one, you should be using the map function, as shown in the official documentation.
This function will give you the index of your element right into its callback, as a the second argument.
Your coded can be reduced to a single line :
const news = this.state.listItems.map((item, index) => <MyComponent key={index} index={index + 1}/>)
Do not forget to set the key prop of your component when making an array.
The <MyComponent index/> syntax is the short version of <MyComponent index={index}/>
You never update your index variable, you're just assigning +1 to the indx variable which is never mutated. What you need is to update your state and increment your indx variable that way and push your value into the array on setState callback, but I recommend you to use the current index array element within your forEach loop as it is the best way to do what you want to :
this.state.listItems.forEach((myListItem, index) => {
news.push(<MyComponent
key={index}
index={index+1}
/>);
});
I saw this in somewhere and I'm confused. I've never do thing this way, what it does actually?
doSomething = index => {
const tempState = this.state
tempState.keys.push(index)
this.setState({
...tempState
})
}
const tempState = this.state
The above assigns to tempState a reference to the state object
tempState.keys.push(index)
tempState has a property of keys that holds a reference to an array and push is called on the array to add index to the array
this.setState({
...tempState
})
this.setState is called and a new copy of the state is passed into setState.
The spread operator is basically copying the content of the old state into a new object.
The above implementation isn't the perfect one and should be improved so that you aren't mutating the original state object.
doSomething = index => {
const tempState = this.state // assing reference of state to tempState
tempState.keys.push(index) // push a value to `keys` property value within state. Note that Javascript object assignment works by reference and hence this will actually mutate the state object
this.setState({
...tempState
}) // call setState to update state and pass the cloned version of tempState using spread syntax as the new state values
}
Although the above implementation clones the value before setState, its incorrect since it should clone the value before any update is made into it
doSomething = index => {
const keys = [...this.state.keys]
keys.push(index)
this.setState({
keys
})
}
However since you use ES5, in order to clone the value, you can make use of concat to update the keys array using functional setState like
doSomething = index => {
this.setState(prevState => ({
keys: prevState.keys.concat([index])
}))
}
I hope the above answer provides an insight into the explanation that you were looking for