Reputation: 221
I have problem with updating my state using useState hook.
So in my "App" component, I declare my array of objects state:
const [panelSettings, setPanelSettings] = useState([
{
title: 'Background',
active: true,
options: [],
updateDaily: true
},
{
title: 'Time and Date',
active: true,
showSeconds: true
},
{
title: 'Weather Forecast',
active: false,
city: null
}])
Then I pass {panelSettings}
and {setPanelSettings}
down to another component, let's call it "Menu".
In this "Menu" component I render each title and have a checkbox next to them, which should set the "active" property. Like so:
{panelSettings.map(element => {
return (
<div key={element.title}>
{element.title}
<input type='checkbox' checked={element.active} onChange={() => setPanelSettings(element.active = !element.active)} />
</div>)
})}
But when I click on any of the checkboxes, I get the error "TypeError: Cannot read property 'active' of undefined". However, it comes from my parent component ("App") and not from the "Menu".
I've tried multiple ways to render out the elements and calling the setPanelSettings
function but nothing has worked so far. I've also logged out the object from the "Menu" component and it seemed like the 'active' property has changed there.
Upvotes: 4
Views: 280
Reputation: 221
The comments put me on the right track, and the final solution was this:
{panelSettings.map(element => {
return (
<div key={element.title}>
{element.title}
<input type='checkbox' checked={element.active} onChange={() => {
const next = [...panelSettings];
next[index].active = !next[index].active;
setPanelSettings(next);
} />
</div>)
})}
I'm sure it's not the most sophisticated, but I don't really know how it could be solved in a more concise way and it got the job done. So thanks everyone for the answers.
Upvotes: 0
Reputation: 3444
Where exactly do you get the error? Is it from the onChange function or from the checked attribute? If it is in the onChange function, you have to use the previous state to update the new state, so it should be something like this:
setPanelSettings((prevState) => {
return prevState.map((menuInput) => {
if (menuInput.title === element.title)
return { ...menuInput, active: !menuInput.active };
return menuInput;
});
})
Upvotes: 0
Reputation: 64725
When you do
setPanelSettings(element.active = !element.active)
you are changing panelSettings
from
[{
title: 'Background',
active: true,
options: [],
updateDaily: true
},
{
title: 'Time and Date',
active: true,
showSeconds: true
},
{
title: 'Weather Forecast',
active: false,
city: null
}]
to either true
or false
. Clearly not what you want.
Probably you want to do:
setPanelSettings((prevState) => prevState.map((s) => {
if (s === element) {
return { ...s, active: !s.active };
}
return s;
}))
Upvotes: 3