I have the following component with a state that keep tracks of checkboxes:
import { useState } from "react";
function SideBar() {
const [queryObject, setQueryObject] = useState({
genders: [],
availabilities: []
});
function handleChange(event) {
const { name, checked } = event.target;
if (name === "male" || name === "female") {
if (checked) {
setQueryObject((prevQueryObject) => {
prevQueryObject.genders.push(name);
return prevQueryObject;
});
} else {
setQueryObject((prevQueryObject) => {
return prevQueryObject.genders.filter((e) => e !== name);
});
}
return;
}
if (name === "available" || name === "not available") {
if (checked) {
setQueryObject((prevQueryObject) => {
prevQueryObject.availabilities.push(name);
return prevQueryObject;
});
} else {
setQueryObject((prevQueryObject) => {
return prevQueryObject.availabilities.filter((e) => e !== name);
});
}
return;
}
}
return (
<div>
<input
type="checkbox"
checked={queryObject.genders.includes("male")}
onChange={handleChange}
name="male"
/>
<span>Male</span>
<input
type="checkbox"
checked={queryObject.genders.includes("female")}
onChange={handleChange}
name="female"
/>
<span>Female</span>
<br />
<input
type="checkbox"
checked={queryObject.availabilities.includes("available")}
onChange={handleChange}
name="available"
/>
<span>Available</span>
<input
type="checkbox"
checked={queryObject.availabilities.includes("not available")}
onChange={handleChange}
name="not available"
/>
<span>Not available</span>
</div>
);
}
export default SideBar;
When I tried to click on the checkboxes it does not update immediately, I found that whenever I save my text file again the browser reloads and then it updates, I think the problem is in my handleChange function, is the way I'm handling the setQueryObject wrong ?
The problem is there in the approach of updating queryObject state . Since queryObject state is an object and if we try to directly modify the object
like this :-
setQueryObject((prevQueryObject) => {
prevQueryObject.genders.push(name);
return prevQueryObject;
});
React does not recognise any change in the state since the object has the same reference so it doesn't update DOM. Therefore we need to create a deep copy of that object and set it back to the object state again so that change in object state is recognised and React updates the DOM.
setQueryObject((prevQueryObject) => {
return{...prevQueryObject,genders:[...prevQueryObject.genders,name]};
});
Full Working Code here - CodeSandbox Link
Thanks to Yury i re-wrote the code and it works
import { useState } from "react";
function SideBar() {
const [queryObject, setQueryObject] = useState({
genders: [],
availabilities: []
});
function handleChange(event) {
const { name, checked } = event.target;
if (name === "male" || name === "female") {
if (checked) {
setQueryObject((prevQueryObject) => {
return {
...prevQueryObject,
genders: prevQueryObject.genders.concat(name)
};
});
} else {
setQueryObject((prevQueryObject) => {
return {
...prevQueryObject,
genders: prevQueryObject.genders.filter((e) => e !== name)
}
});
}
return;
}
if (name === "available" || name === "not available") {
if (checked) {
setQueryObject((prevQueryObject) => {
return {
...prevQueryObject,
availabilities: prevQueryObject.availabilities.concat(name)
};
});
} else {
setQueryObject((prevQueryObject) => {
return {
...prevQueryObject,
availabilities: prevQueryObject.availabilities.filter(
(e) => e !== name
)
};
});
}
return;
}
}
return (
<div>
<input
type="checkbox"
checked={queryObject.genders.includes("male")}
onChange={handleChange}
name="male"
/>
<span>Male</span>
<input
type="checkbox"
checked={queryObject.genders.includes("female")}
onChange={handleChange}
name="female"
/>
<span>Female</span>
<br />
<input
type="checkbox"
checked={queryObject.availabilities.includes("available")}
onChange={handleChange}
name="available"
/>
<span>Available</span>
<input
type="checkbox"
checked={queryObject.availabilities.includes("not available")}
onChange={handleChange}
name="not available"
/>
<span>Not available</span>
</div>
);
}
export default SideBar;