1

I have this CheckBox component that is a simple checkbox button

<CheckBox
  className={'w-100'}
  checked={isChecked(item.id)}
  onClick={() => updateItem(item.id)}
/>

As you can see, it takes two functions isChecked and updateItem. Those functions are defined as

const isChecked = useCallback(
  (itemId: string) => {
    return state.some((id) => id === itemId);
  },
  [state]
);

const updateItem = useCallback(
  (item) => {
    setState((myArray) => [...myArray, item]);
    console.log(state, item);
  },
  [setState, state]
);

where const [state, setState] = useState<string[]>([]);. What I'm trying to achieve is the following: If you click on the checkbox, add that item id inside the array that is kept in the state and mark it as checked. However, console.log(state, item) prints [], 'item id' all the time inside this updateItem as it can't read the state inside it. The same thing goes for isChecked, state is empty all the time. However, if I console.log it somewhere outside of these functions, in the component body, I get an array of item ids. Have any ideas what am I doing wrong because from all the things I can see, it seems that most of the things are set up correctly and I don't know from where this behavior origins. Thanks in advance

anthino12
  • 770
  • 1
  • 6
  • 29
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Rashomon Feb 23 '22 at 14:16
  • I don't think it does. I'm doing what's described in the solution comment, still getting empty array in ALL functions except the component body (by this I mean a console.log outside of any function) – anthino12 Feb 23 '22 at 14:20
  • This basically could depend on the context your memoized callbacks are being used. Can you share a basic version of your code that reflects how and when those methods are being used? – Rodrigo Feb 23 '22 at 14:46

1 Answers1

-1

When a checkbox is clicked it is added to the state. But when you click it again, it needs to be removed. If you design the component in a different way, I think the codes can be improved. Run the program below and click some to see the state change.

function useCheckbox(initState = false) {
  const [checked, setChecked] = React.useState(initState)
  return [
    checked,
    <input type="checkbox" checked={checked} onClick={_ => setChecked(!checked)} />
  ]
}

function App() {
  const [inStock, stockCheckbox] = useCheckbox(false)
  const [freeShip, freeShipCheckbox] = useCheckbox(false)
  const [onSale, onSaleCheckbox] = useCheckbox(false)
  return <div>
    In Stock: {stockCheckbox}<br/>
    Free Shipping: {freeShipCheckbox}<br/>
    On Sale: {onSaleCheckbox}<br/>
    <pre>{JSON.stringify({ inStock, freeShip, onSale })}</pre>
  </div>
}

ReactDOM.render(<App/>, document.querySelector("#app"))
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>

Here's another option using a variation of the technique above. In this one a generic useToggle is created and used for each checkbox's state. A Checkbox component is made to give us precise control over a checkbox's representation in the app. The checkbox states can be controlled using props on and off.

function useToggle(initState = false) {
  const [state, setState] = React.useState(initState)
  return [state, _ => setState(!state)]
}

function Checkbox({ checked, onClick, on = "✅", off = "❌" }) {
  // use <input type="checkbox"> or any custom representation
  return <div className="checkbox" onClick={onClick}>
    {checked ? on : off}
  </div>
}

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>
}

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>
よつば
  • 467
  • 1
  • 8