I'm using React's Strict mode, and I'm having a useEffect that is causing me some headaches. Part of React's Strict mode is that those things can fire twice, so I'm writing code to protect myself from that.
However I seem to be missing something when using useReducer. By using useState I'm able to update the state so that useEffect becomes idempotent.
With useReducer I dispatch an action which only seems to be executed AFTER useEffect has rendered twice, effectively nullifying my idempotency.
useState example (pardon the React setup cruft, StackOverflow doesn't support React 18 natively):
const { StrictMode, useState, useEffect } = React;
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<StrictMode>
<WithUseState />
</StrictMode>
);
function WithUseState() {
const [counter, setCounter] = useState(0);
useEffect(() => {
if (counter === 0) {
setCounter(counter + 1);
}
}, [counter]);
return <React.Fragment>{counter}</React.Fragment>;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
useReducer example, where the order seems to be: useEffect, useEffect, resolve dispatch, resolve dispatch:
const { StrictMode, useEffect, useReducer } = React;
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<StrictMode>
<WithUseReducer />
</StrictMode>
);
function reducer(state, action) {
switch (action.type) {
case "increment":
return { ...state, counter: state.counter + 1 };
default:
throw new Error();
}
}
function WithUseReducer() {
const [state, dispatch] = useReducer(reducer, { counter: 0 });
useEffect(() => {
if (state.counter === 0) {
dispatch({ type: "increment" });
}
});
return <React.Fragment>{state.counter}</React.Fragment>;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>