Is it, and why is it a bad idea to mutate a state from React's new useState hook? I found no info on the topic.
Consider following code:
const [values, setValues] = useState({})
// doSomething can be called once, or multiple times per render
const doSomething = (name, value) => {
  values[name] = value
  setValues({ ...values })
}
Note the mutation of values. Since doSomething can be called more than once per render, doing this would not work because of the async properties of setState:
const doSomething = (name, value) => {
  setValues({ ...values, [name]: value })
}
Is the approach of mutating values directly the correct one in this case?
You should never mutate state directly as it might not even cause a re-render if you update the state with the same object reference.
const { useState } = React;
function App() {
  const [values, setValues] = useState({});
  const doSomething = (name, value) => {
    values[name] = value;
    setValues(values);
  };
  return (
    <div onClick={() => doSomething(Math.random(), Math.random())}>
      {JSON.stringify(values)}
    </div>
  );
}
ReactDOM.render(<App />, document.getElementById("root"));<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>You can give a function as first argument to setValues just like you are used to in the class component setState, and that function in turn will get the correct state as argument, and what is returned will be the new state.
const doSomething = (name, value) => {
  setValues(values => ({ ...values, [name]: value }))
}
const { useState } = React;
function App() {
  const [values, setValues] = useState({});
  const doSomething = (name, value) => {
    setValues(values => ({ ...values, [name]: value }));
  };
  return (
    <div onClick={() => doSomething(Math.random(), Math.random())}>
      {JSON.stringify(values)}
    </div>
  );
}
ReactDOM.render(<App />, document.getElementById("root"));<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>Basically I would avoid mutating state in such way simply for the sake of purity.
However, I would argue that in this case it is perfectly fine. When you mutate a value in an inner level in the state it goes unnoticed by React. Only when calling setValues() with a new reference React notes to itself that a new render is pending.
const { useState } = React;
function App() {
  const [values, setValues] = useState({ num: 0 });
  const handleClick = () => {
    doSomething();
    doSomething();
  }
  
  const doSomething = () =>
    setValues((values) => {
      values.num += 1;
      return { ...values };
    });
return (
  <div onClick={handleClick}>
    {JSON.stringify(values)}
  </div>
);
}
ReactDOM.render( < App / > , document.getElementById("root"));<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>(If anyone can provide a counter example I would love to see it).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With