I'm not sure if this is expected behavior, but if you bail out of a dispatch (https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-dispatch) while using the useReducer hook, the action occurs twice if it's followed by a render. Let me explain:
// bailing out to prevent re-rendering
const testReducer = (state, action) => {
switch (action.type) {
case "ADD":
state.test += 1
return state;
}
};
const myComponent = () => {
let [totalClicks, setClicks] = useState(0);
const [state, setState] = useReducer(testReducer, {
test: 0,
});
const clickHandler = () => {
setState({type: 'ADD'});
setClicks((totalClicks += 1));
};
return (
<div>
<button onClick={clickHandler}>+</button>
<p>{totalClicks}</p>
<p>test count: {state.test}</p>
</div>
);
}
When you click the button, state.test goes up by 2, while totalClicks increases by 1. However, if I were to change the reducer so that it doesn't bail to one like below, they would both increase by 1.
// non-bailing reducer
const testReducer = (state, action) => {
switch (action.type) {
case "ADD":
return {
test: state.test + 1,
};
}
};
Why is this? And is this expected behavior or a bug? sandbox example: https://codesandbox.io/s/sad-robinson-dds63?file=/src/App.js
UPDATE: after doing some debugging, it looks like this behavior only occurs when wrapped with a React.StrictMode
Does anyone know what causes this???
According to the doc of StrictMode, react intentionally calls reducer function with same action twice, so to expose unnoticed potentially harmful side-effects, which is exactly what happens to your case.
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions: […] Functions passed to useState, useMemo, or useReducer