UseReducer is an alternative to useState. When useState does not meet our needs well, useReducer may solve our problems.

usage

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

The first reducer is the function (state, action) => newState, which accepts the current state and action. The second parameter initialArg is the initial state value. The third argument init is the lazy initialization function.

Use an official example to see how it works

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>
    </>
  );
}
Copy the code

The parameters of dispatch are the Reducer actions. The Reducer function performs some logic based on the incoming actions and returns the new state.

use

  • Dispatches can be passed to child components in place of callbacks for updates from the bottom up. The advantage is that dispatch is not redefined during updates, which reduces the overhead of redefining callback functions and makes it easier for sub-components to determine whether updates are needed based on props.

    If the hierarchy is too deep, it can also be used with context, where using Dispatch instead of callback is more advantageous. Because dispatch is invariant in re-render, it does not cause components using the context to perform meaningless updates.

  • React does not process updates outside events in batches. Reducer can be used to avoid this problem.

      const reducer = (state, action) = > {
        switch(action.type) {
          case 'update':
            return {
              ...state,
              data: action.payload.data,
              loading: false,}default:
            returnstate; }}// The event is triggered
      dispatch({ type: 'update'.payload: { data }})
    
      // Or simply
      const reducer = (state, newState) = > {
        return{... state, ... newState} }const [state, dispatch] = useReducer(reducer, {loading: true.data: null.something: ' '})
      / / triggers
      dispatch({loading: false})
    Copy the code

    The essence is to put state in the same place, but more readable than useState.

  • ForceUpdate useState and useReducer skip updates if they have the same value. There is no mandatory update method for function components. We can use useReducer to simulate one.

      const [, forceUpdate] = useReducer(x= > x + 1.0)
    
      function onClick = () = >{
        forceUpdate()
      }
    Copy the code

conclusion

UseReducer is an alternative to useState. It does everything useState can do and does it better. UseReducer decouples the logic of actions from subsequent actions (typically UI updates) to a certain extent, resulting in more code but a cleaner look.