Update inside React useState setter being called twice

Viewed 849

I'm trying to update something inside the scope of the useState setter. This works as I would expect in the following codepen example:

Update value outside of state variable

But as implemented in my application the insideCallCount is getting called twice for every toggleCell call. See below for the relevant portion:

  const callCount = useRef(0);
  const insideCallCount = useRef(0);
  const toggleCell = (i) => {
    callCount.current += 1;
    setPattern((pattern) => {
      let newPattern = deepCopyPattern(pattern);
      newPattern[i][selectedSound].on = !newPattern[i][selectedSound].on;
      insideCallCount.current += 1;
      return newPattern;
    });
    console.log('callCount: ', callCount.current);
    console.log('insideCallCount: ', insideCallCount.current);
  };

The console readout looks like this after I 'toggle' a button several times:

callCount:  1
insideCallCount:  0
callCount:  2
insideCallCount:  2
callCount:  3
insideCallCount:  4
callCount:  4
insideCallCount:  6
callCount:  5
insideCallCount:  8
callCount:  6
insideCallCount:  10

My understanding this has something to do with the fact that it's wrapped in a function definition. I tried wrapping the whole thing in a useCallback with values to watch inside the dependency array but that didn't help. What am I missing?

1 Answers

As is usually the case when things start unexpectedly reoccuring in a React App: make sure StrictMode is disabled. You'll find it wrapped around <App/> by default in your index.js file on new create-react-app builds. I can only reproduce/fix this by creating a new project with it and then removing it.

Whether you want to leave it off is up to you. It will not be included when you create a production build and can help identify potential problems in your application. In the course of testing your code it will double-invoke various lifecycle methods (in class components) and hooks (in functional components) to highlight where unexpected side-effects may occur in future versions of React.

For this reason, the documentation recommends not performing side effects in any of the following:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

This list is taken from the documentation on StrictMode.

Related