Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React hook useRender called twice if bailing and setting state afterward

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

like image 906
webbyweb Avatar asked Jan 18 '26 19:01

webbyweb


1 Answers

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

like image 108
hackape Avatar answered Jan 21 '26 09:01

hackape