React useReducer Hook - (Part - I)

useReducer is a hook comes with React, which is similar to useState to use with functional components. It is not redux but can provide a similar experience of a centralized store manipulation option. useReducer should be used for complex setup unlike useState, which works really well for a simpler one.

Difference between useState(“value”) and useReducer(reducer, action) is that whenever we want to use old state, we must use dispatch and it must go through a reducer.

Reducer

Reducer is a function which takes the old value and a action associated with it as an input and returns a new state.

We will continue with out Carousel example but will refactor it a bit.

Lets define our component state object as below:

 
  // Component State Object
  const defaultState = {
    url: "http://localhost:80/books.json",
    books: [],
  };

  // reducer function
  const reducer = (prevState, action) => {
    //define actions here using switch-case
  };

  // reducer function with default state value associated with it
  const [state, dispatch] = useReducer(reducer, defaultState);


We have defined defaultState as a object and moved books, url inside it. We also added isModalOpen and modalContent properties alongside which we will use later. defaultState will be only used to initialize our state with the default values, quite similar to useState.

We have also defined our reducer function, which will have previous state of our state object and will also take a action object.

Finally, we are using useReducer to initialize default values of our state object using defaultState and the reducer function which we have created earlier. useReducer is looking for a reducer with a function that will manipulate state object, which will happen when we dispatch action.

Please note that the name state is to represent a component model, and it can be named anything of your choice but as a standard practice, it is suggested to be called as state.

Dispatch

In order to change state, we need to dispatch an action.

Dispatch takes a object with a property named type which will be our action/custom event trigger to the reducer.

We can define multiple such custom actions.

A standard convention is to use uppercase with action names but its not necessary.

Once an event is dispatched (sent), it needs to be handled in reducer.

With dispatch we can optionally can pass the payload, which is data associated with that action. Please note that payload is also not a keyword, you can use any name of your choice.

 
    dispatch({ type: "CHANGE_URL" });    
    dispatch({ type: "UPDATE_BOOKS", payload: newBooks });  


Reducer


  // reducer function
  const reducer = (prevState, action) => {
    //define actions here using switch-case
  };


Reducer has access to previous state, which we can use to perform any operation.

Here we can provide a meaning to what action we are trying to do.

This function is expected to handle actions/custom events dispatched.

Please note that a reducer function must return some state otherwise React will throw an error!

We can use if-else or switch or any other conditional operators of your choice for iterating over actions.

In case the action couldn't be resolved, we can raise a custom error.

In our example, based on state changes required, we will have following actions:

  • UPDATE_BOOKS
    • We will use this whenever we want to change the list of books to render,
    • We can dispatch this action from useEffect post fetch call is complete similar to useState implementation
  • CHANGE_URL
    • We will use this when user clicks on the button to change the URL of our books API

So at a high level, a dispatcher dispatches an action associated with a state change and reducer is expected to capture that action and accordingly update specific state and return new state back to the caller.

This discriminates directly working with useStates and make state management a more structured, centralized state service.








Comments

Popular Posts