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