Can I recreate the "reducer" for the useReducer hook on every render?

Viewed 346

This is the example from: https://reactjs.org/docs/hooks-reference.html#usereducer

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

In this example, the reference for the reducer function stays the same across renders.

Can I do this to recreate the reducer function on every render?

const initialState = {count: 0};

function Counter() {

  // REDUCER FUNCTION WILL BE RECREATED ON EVERY RENDER

  function reducer(state, action) {
    switch (action.type) {
      case 'increment':
        return {count: state.count + 1};
      case 'decrement':
        return {count: state.count - 1};
      default:
        throw new Error();
    }
  }

  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

MOTIVE:

I'm working on a currency converter, and my reducer depends on the THOUSAND_SEPARATOR and DECIMAL_SEPARATOR being dots . or commas ,, which might change between renders, so I need to recreate it on every render.

SNIPPET

It seems to work, but is it an anti-pattern?

function App() {

  const initialState = {count: 0};

  const [bool,setBool] = React.useState(false);

  function reducer(state, action) {
    switch (action.type) {
      case 'increment':
        return {count: bool ? state.count - 1 : state.count + 1};
      case 'decrement':
        return {count: bool ? state.count + 1 : state.count - 1};
      default:
        throw new Error();
    }
  }

  const [state,dispatch] = React.useReducer(reducer, initialState);

  return (
    <React.Fragment>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => setBool((prevState) => !prevState)}>Invert Direction</button>
    </React.Fragment>
  );
}

ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

1 Answers

That approach is fine.

As I understand it, JavaScript engines are pretty good at reusing the function bodies even if function objects are created multiple times. The main difference is that the function defined inside of the render function will be referencing an additional closure (variables declared in the render function), so it'll be a bit less efficient. But since you need it to reference your component's state, it's a reasonable solution.

Related