Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass inline reducer function to React.useReducer

Tags:

reactjs

Let's take following example with an inline reducer:

const App = () => {
  const [state, dispatch] = React.useReducer(s => {
    console.log("reducer invoked with state", s)
    return s + 1
  }, 0);

  return (
    <div>
      <p>{state}</p>
      <button onClick={dispatch}>Increment</button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>

On first click, App is rendered twice, subsequent clicks cause only one re-render.

I would like to understand above behavior of useReducer. After having read this answer, some things are still not clear to me.

  1. Why is a double render triggered only first time for useReducer?
  2. Are there additional circumstances to 1.), which cause double rendering for an inline reducer as well?
  3. When would this inline reducer pattern be not recommended (performance, etc.)? Or is it without flaws?
like image 402
bela53 Avatar asked Jul 04 '26 19:07

bela53


1 Answers

By putting the reducer function inside the component, you make it so each time the component renders a new function is created. It may have the same text, but react can't tell that, so it's forced to run the old and new functions and compare their results.

In the following snippet i've made the reducer function log out which instance of the function is running, and you'll see that those first two double-calls are two different functions. I'm not sure why it doesn't need to also do this on later times

let count = 0;
const App = () => {
  count++;
  const [state, dispatch] = React.useReducer(s => {
    console.log(`reducer #${count} invoked with state`, s)
    return s + 1
  }, 0);

  return (
    <div>
      <p>{state}</p>
      <button onClick={dispatch}>Increment</button>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>

So yes, using an inline reducer can reducer performance a little bit. As a result, if you don't need to inline it, then you should move it outside the component.

If you do need to inline it (say, because you need to refer to other variables inside the component), it's probably not a big deal in most cases. If it is, then you can useCallback to memoize it and only change it when needed.

const Example = () => {
  const [size, setSize] = useState(1);
  const reduction = useCallback((s) => {
    console.log(`reducer called; something = ${something}`);
    return s + size;
  }, [something]l
  const [state, dispatch] = useReducer(reduction, 0);
like image 88
Nicholas Tower Avatar answered Jul 06 '26 16:07

Nicholas Tower



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!