Search code examples
javascriptreactjsreduxredux-toolkit

Redux selector function triggered multiple times


Code Link: https://codesandbox.io/s/charming-colden-3browz?file=/src/App.js

I just get started with Redux, and I have a slice defined in src/store/sizeSlice.js, and my redux store is in src/store/store.js. Now the idea is when the two buttons (addCol, addRow) get clicked, they will dispatch actions to increment the global states of the number of rows and columns. I use two useSelector hooks in App.js.

Now whenever I click one of the two buttons, there will be four outputs to the console. For example, if I click addRow, there will always be one console log of selectRow, and three logs of selectCol. I know that the useSelector gets called when the function component, in this case App.js rerenders. There should only be two outputs (one selectRow, and one selectCol), but why is the selector function getting called three times? Especially the button that is related to it (dispatch addCol action in this case) didn't get clicked?


Solution

  • React can, and will, on occasion "render" the app more than once during the "Render Phase" to compute a diff that should be rendered, i.e. flushed, to the DOM during the "Commit Phase". The "Commit Phase" is what we generally consider to be a React component "rendering", because this is when we see the UI update.

    enter image description here

    Why is this distinction important?

    Note that the entire React Function component body is the "render" method, and the "render" method is called during the "Render Phase" which "may be paused, aborted, or restarted by React". This means React may call the "render" method as many times as it needs to in order to compute a diff during the reconciliation process, i.e. when state updates have been enqueued and are processed.

    You are console logging as an unintentional side-effect in the useSelector hook callback.

    These logs should not be confused for the component rerendering or being rendered to the DOM multiple times. The logs are unintentional side-effects of the "Render Phase" which can occur several times.

    If you care to log state updates as a result of state update -> render to DOM then move all console logs into a useEffect hook. The useEffect hook is for running intentional side-effects. 1 React component render == 1 intentional side-effect.

    App example:

    function App() {
      const col = useSelector(selectCol);
      const row = useSelector(selectRow);
    
      useEffect(() => {
        console.log("Count:", { col, row });
      }, [col, row]);
    
      return (
        <div className="container">
          <Row />
          <Addcolumn />
          <Addrow />
        </div>
      );
    }
    

    Edit redux-selector-function-triggered-multiple-times