I have a functional component with a state hook called "checked" that is an array of Booleans for a list of checkboxes. When a checkbox is clicked, I have an event handler that updates state so the checkbox will render a check or an empty box.
According to the console.logs in my function, state is updating, but the checkboxes are not responsive in the browser and dev tools confirm that state is not updating. If I manually toggle state using the dev tools the checkboxes work just fine, so I believe it's an issue with state not updating. Any advice would be very appreciated!
import React, { useState, useContext } from 'react';
export default function MessengerCheckboxes() {
const [checked, setChecked] = useState([false, false, false]);
...
const handleChangeChild = (event) => {
console.log('Currently state is: ' checked); // Currently state is: [false, false, false]
let index = parseInt(event.target.id.slice(0, 1));
let localChecked = checked; //
localChecked[index] = !checked[index];
console.log('State should be: ' localChecked); //State should be: [true, false, false] [[for example]]
setChecked(localChecked);
setTimeout(() => { // Added setTimeout for troubleshooting to make sure I wasn't calling state too quickly after setting it
console.log('State is now: ' checked); // State is now: [true, false, false] [[but won't trigger re-render and dev tools show state is [false, false, false]]
}, 1 * 1000);
};
}
A huge thank you in advance!
CodePudding user response:
you shouldn't update state like that.
In situations like this, a bit more complex state object is required. I like to use an object to keep state for each item in the list. Check this link: https://codesandbox.io/s/checkbox-state-https-stackoverflow-com-questions-69680938-react-js-state-not-updating-in-functional-component-di0dd
import "./styles.css";
import { useState } from "react";
export default function App() {
// 1.
const stateObj = {
cb1: false,
cb2: false,
cb3: false
};
const [cbState, setCbState] = useState(stateObj);
const handleCb = (event) => {
console.log(event.target.name, event.target.checked);
// 4.
setCbState({ ...cbState, [event.target.name]: event.target.checked });
};
return (
<div>
<input
type="checkbox"
name="cb1" // 2.
onChange={handleCb}
value={cbState["cb1"]} // 3.
/>
<input
type="checkbox"
name="cb2"
onChange={handleCb}
value={cbState["cb2"]}
/>
<input
type="checkbox"
name="cb3"
onChange={handleCb}
value={cbState["cb3"]}
/>
</div>
);
}
So for take away, in steps:
- create/prepare state object - keys will be used as names to html elements
- set name attribute for html element - use same keys from 1.
- set value attribute for html element - bracket notation using
state['key']
. This is how you achieve controlled components - set state in a way that you persist existing data/values (using spread operator) update (bracket notation for accessing the properties). In this example we achieve it with event
name
andchecked
attributes.
CodePudding user response:
Well you'd have read the comments that you shouldn't mutate the state. So to handle such situations, I usually to do like this:-
setChecked(data => ({
...data,
[ind]: !checked[ind]
}))
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
P.S. - Do read about different approaches of handling states, it's gonna help you alot in the longer run. :)