Reputation: 169
I'm trying to use setState in map function what I want to make when I hit click button that makes states changed
I searched "In map function changed setState" in stackoverflow
but It was hard to understand..and apply my case
this.state = {
categories: [
{
category: 'sports',
ariticles: [],
isClick: true
},
...
...
return (
<div>
<hr></hr>
<div className="categoryBtn">
{this.state.map(item => (
<button
onClick={
() => this.setState(() => (item.isClick = false))
// () => console.log((item.isClick = false))
// this.setState(() => (item.isClick = !item.isClick))
}
>
{' '}
{item.category}
</button>
))}
when I hit my button I got error ' this.state.map is not a function'
I expect when I hit button, isClick will be toggle
Upvotes: 3
Views: 2577
Reputation: 36895
Your this.state
refers to an object containing an array,
categories: [...]
So you'd need to map over this.state.categories.map
instead of this.state.map
And also, I've noticed that button
event handler mutates the state.
<button onClick={() => this.setState(() => (item.isClick = false))}>
{item.category}
</button>
You'd need to return a new item reference
to notify react that the state has been changed as well as adding a key
prop.
Thankfully, someone asked a question about the reason for using key (only 2 hours ago) and replied, which you can refer to.
https://stackoverflow.com/a/57838612/4035
<button key={item.category} onClick={() => this.setState({
find the item by searching for each item in `this.state.cateogies`
})}>
{item.category}
</button>;
But then it gets tough to find the right element, so I'd recommend to normalize(meaning change the this.state.categories
) too look like following for easier way to set the state.
// From this
this.state = {
categories: [
{
category: "sports",
ariticles: [],
isClick: true
},
{
category: "sports",
ariticles: [],
isClick: true
}
]
};
// to this
this.state = {
categories: {
sports: {
category: 'sports',
ariticles: [],
isClick: true
},
news: {
category: 'news',
ariticles: [],
isClick: true
}
}
};
This way, you can set the state like following.
<button
key={item.category}
onClick={() =>
this.setState({
[item.category]: {
isClick: false
}
})
}
>
{item.category}
</button>
Reply to the comment below (I've translated the comment for non-Korean speakers)
So you can't use
.map
any more after making such a change?
I am sorry, I should've updated the category item generation code after the state update.
No, you can't use .map
as it's now an object. But thankfully, you can use either Object.keys/value/entries
to iterate the this.state.categories
.
You can click on "Run code snippet" button to see the output
let state = {
categories: {
sports: {
category: "sports",
ariticles: [],
isClick: true
},
news: {
category: "news",
ariticles: [],
isClick: true
}
}
};
Object.entries(state.categories).map(([id, item]) => console.log(item))
So your code would look like something like,
<div className="categoryBtn">
{Object.entries(state.categories).map(([id, item]) => (
<button
key={id}
onClick={() =>
this.setState({
[item.category]: {
isClick: false
}
})
}
>
{item.category}
</button>
))}
</div>
Upvotes: 4