useReducer :

The useReducer hook is a more complex and advanced way of managing state instead of the useState hook.

  • It works with a so called reducer function, which is a pure function which will take the "previous state" and the "action" as an argument and will then return the next state.

Example :


const [count, dispatch] = useReducer(reducer, 0); // useReducer(function, initial state)
  // now when is the reducer function called ? This is where "dispatch" comes into play, "dispatch" function can also be used to update the state.

reducer function :

function reducer(state, action) {
  console.log(state, action);
}

dispatch function :

const inc = function () {
    dispatch(1); // this 1 is going to become the "action" in the reducer function
    // setCount((count) => count + 1);
    // setCount((count) => count + step);
  };
  • Output :
  • Pasted image 20240306200711.png
  • We get the output as 0 1 because the state and action are 0 and 1.
dispatch(x); // "x" becomes the "action" in the reducer function
// the "state" in the reducer function is the previous state.
  • The idea of the reducer is to take these two things "current state" and "action" and return the next state based on these two.

Using Objects in dispatch function : type & payload :

  const [count, dispatch] = useReducer(reducer, 0); // useReducer(function, initial state)
const dec = function () {
    dispatch({ type: "dec", payload: +1 }); 
    // setCount((count) => count - 1);
    // setCount((count) => count - step);
  };

  const inc = function () {
    // dispatch(1); // this 1 is going to become the "action" in the reducer function
    dispatch({ type: "inc", payload: +1 }); 
    // setCount((count) => count + 1);
    // setCount((count) => count + step);
  };

  const defineCount = function (e) {
    // setCount(Number(e.target.value)); // when the input is empty, it will be 0
    dispatch({type: "setCount", payload: Number(e.target.value)})
  };

reducer function :

function reducer(state, action) {
  console.log(state, action);

  if(action.type === 'inc') return state + action.payload;
  if(action.type === 'dec') return state - action.payload;
  if(action.type === 'setCount') return action.payload;
}

Info

if an object is used as a state in a reducer function that an object of same structure has to be returned from that reducer function.

for example :

function reducer(state, action) {
  console.log(state, action);

  switch (action.type) {
    case "dec":
      return { ...state, count: state.count - 1 }; // here we are destructuring the state object and then overwriting the count property
    case "inc":
      return { ...state, count: state.count + 1 };
    case "setCount":
      return { ...state, count: action.payload };
    case "setStep":
      return { ...state, step: action.payload };
    case "reset":
      return {count: 0, step: 0};
    default:
      throw new Error("Unknown action type");
  }
}
  const initialState = { count: 0, step: 0 };
  const [state, dispatch] = useReducer(reducer, initialState);
  const { count, step } = state;
  const defineStep = function (e) {
    // setStep(Number(e.target.value));
    dispatch({ type: "setStep", payload: Number(e.target.value) });
  };

  const reset = function () {
    // setCount(0);
    // setStep(1);
    // instead of setting two states, we can now set both states at once using useReducer
    dispatch({type: "reset"});
  };

Pasted image 20240519235949.png

Pasted image 20240520000208.png

See react-quiz project for useReducer implementation