'React.Js - useCallback inside a callback generator
I have a bunch of checkbox components and I want to update a state of the parent component each time the checkbox components are toggled. Right now I'm doing this:
const selChangeHandler = useCallback(
(checkboxId, isSel) => {
if (isSel) setSelectedCheckboxes((prevState) => [...prevState, checkboxId]);
else
setSelectedCheckboxes((prevState) =>
prevState.filter((x) => x != checkboxId)
);
},
[setSelectedCheckboxes]
);
This function is passed into each checkbox. I don't think that this is a particularly good approach as each checkbox can add anything to the array rather than only its id. So I tried this but I got an error:
const GetSelChangeHandler = (checkboxId) => {
return useCallback(
(isSel) => {
if (isSel) setSelectedCheckboxes((prevState) => [...prevState, checkboxId]);
else
setSelectedCheckboxes((prevState) =>
prevState.filter(
(x) => x != checkboxId
)
);
},
[setSelectedCheckboxes]
); };
I executes this inline and passed the returned handler in as a callback prop to my checkbox components. I'm not sure what the error was but something to do with violating the rules of hooks. What is the correct way of doing this?
Solution 1:[1]
You cannot call hooks in loops or conditionals. How about something like a useToggle custom hook that makes it easier to manage the state of many checkboxes? Run the code below, click some ? and see how the application state reflects the changes.
function useToggle(initState = false) {
const [state, setState] = React.useState(initState)
return [state, _ => setState(!state)]
}
function App() {
const [inStock, toggleInStock] = useToggle(true)
const [freeShip, toggleFreeShip] = useToggle(true)
const [onSale, toggleOnSale] = useToggle(true)
return <div>
In Stock: <Checkbox checked={inStock} onClick={toggleInStock} /><br/>
Free Shipping: <Checkbox checked={freeShip} onClick={toggleFreeShip} /><br/>
On Sale: <Checkbox checked={onSale} onClick={toggleOnSale} on="?" /><br/>
<pre>{JSON.stringify({ inStock, freeShip, onSale })}</pre>
</div>
}
function Checkbox({ checked, onClick, on = "?", off = "?" }) {
// use <input type="checkbox"> or any custom representation
return <div className="checkbox" onClick={onClick}>
{checked ? on : off}
</div>
}
ReactDOM.render(<App/>, document.querySelector("#app"))
.checkbox { display: inline-block; cursor: pointer; }
pre { background-color: #ff8; padding: 0.5rem; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
If the amount of checkboxes is unknown, a useToggles variation can solve the problem.
function useToggles(init = {}) {
const [states, setStates] = React.useState(init)
return [
states,
key => event => setStates({...states, [key]: !Boolean(states[key])})
]
}
function App() {
const [checkboxes, toggle] = useToggles({
inStock: true,
freeShip: true,
onSale: true
})
return <div>
In Stock:
<Checkbox checked={checkboxes.inStock} onClick={toggle("inStock")}/><br/>
Free Shipping:
<Checkbox checked={checkboxes.freeShip} onClick={toggle("freeShip")}/><br/>
On Sale:
<Checkbox checked={checkboxes.onSale} onClick={toggle("onSale")} on="?"/><br/>
<pre>{JSON.stringify(checkboxes)}</pre>
</div>
}
function Checkbox({ checked, onClick, on = "?", off = "?" }) {
// use <input type="checkbox"> or any custom representation
return <div className="checkbox" onClick={onClick}>
{checked ? on : off}
</div>
}
ReactDOM.render(<App/>, document.querySelector("#app"))
.checkbox { display: inline-block; cursor: pointer; }
pre { background-color: #ff8; padding: 0.5rem; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
