Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Hooks. Pass value as argument to useReducer()

It is my first time using useReducer() hook and I am facing a problem where I need to pass it a value as argument.

Here is how my reducer looks like:

  const memoizedReducer = useCallback((state, action) => {
    switch (action.type) {
      case "save":
        const refreshedBookData = {
          ...state.bookData,
          ...(state.bookData.totalSaves >= 0 && {
            totalSaves: state.bookData.totalSaves + 1,
          }),
          isSaved: true,
        };
       
        // Add the new book data to a Map which I have in a Context provider
        currentUser.addBookToVisitedBooks(bookId, refreshedBookData);
        
        // Update my context provider data
        currentUser.setData((prevCurrentUserData) => {
          return {
            ...prevCurrentUserData,
            ...(prevCurrentUserData.totalSaves >= 0 && {
              totalSaves: prevCurrentUserData.totalSaves + 1,
            }),
          };
        });
        
        return refreshedBookData;

    case "unsave":
        const refreshedBookData = {
          ...state.bookData,
          ...(state.otheBookData.totalSaves >= 0 && {
            totalSaves: state.bookData.totalSaves - 1,
          }),
          isSaved: false,
        };

        // Add the new book data to a Map which I have in a Context provider
        currentUser.addBookToVisitedBooks(bookId, refreshedBookData);
      
        // Update my context provider data
        currentUser.setData((prevCurrentUserData) => {
          return {
            ...prevCurrentUserData,
            ...(prevCurrentUserData.totalSaves > 0 && {
              totalSaves: prevCurrentUserData.totalSaves - 1,
            }),
          };
        });
        
        return refreshedBookData;
      
    default:
        return state;
});


const [{ bookData }, dispatch] = useReducer(memoizedReducer, {
   bookData: params?.bookData
});

As you can see, what I am doing is:

1- If the action type is "save", increase the total saves for the book, add the new book data to a Map of "visitedBooks" which I have in a Context (ignore this part), and update my currentUser data increasing his total saves.

2- If the action type is "unsave", I do the opposite.

My problem is that I need to pass an argument "reviews" to the reducer. For example, if I have this function for fetching "extra data" from my db:

   const fetchReviews = async (bookId) => {
       // Get a list of reviews from the db
       const reviews = await db.fetchReviews(bookId);

       return reviews;
   }  

and I use it like this:

  const reviews = await fetchReviews(bookId);
  

how can I pass the reviews as argument to the reducer?

  dispatch({ type: saved ? "save" : "unsave" }); 

Thank you.

like image 264
Raul Avatar asked Nov 01 '20 00:11

Raul


People also ask

How do you use useReducer in React hooks?

Syntax. The useReducer Hook accepts two arguments. The reducer function contains your custom state logic and the initialState can be a simple value but generally will contain an object. The useReducer Hook returns the current state and a dispatch method.

What is returned by the useReducer () Hook?

1. useReducer() The useReducer(reducer, initialState) hook accept 2 arguments: the reducer function and the initial state. The hook then returns an array of 2 items: the current state and the dispatch function.

How many arguments does useReducer take?

The useReducer hook requires 2 arguments, and has an optional 3rd argument: reducer - a pure function that takes a state and an action, and returns a new state value based on the action. initialState - any initial state value, just like useState.

Can I use useState and useReducer together?

The useReducer hook is usually recommended when the state becomes complex, with state values depending on each other or when the next state depends on the previous one. However, many times you can simply bundle your separate useState statements into one object and continue to use useState .


Video Answer


2 Answers

You already pass down an object to dispatch() and nothing stops you to add a payload along with the type:

dispatch({ 
  type: saved ? "save" : "unsave",
  payload: reviews,
});

This way you can access the reviews in your reducer (action.payload).

const reducer = (state, action) => {
  // action has `type` and `payload` properties
}
like image 118
Zsolt Meszaros Avatar answered Oct 17 '22 21:10

Zsolt Meszaros


When you call the dispatch method, you're passing an object which has the type field. The format of this object is actually up to you to define. If you need to pass parameters other than type you can freely do so.

For example


const reviews = {....} ; /// reviews  
dispatch({ type: 'save', payload: reviews });

Then in your reducer you can you the payload object

// Reducer 

const reducer = (state, action) => {
    switch (action.type) {
        case 'save':
            const reviews = action.payload;
            //.....
            break;
    }
}

I suggest reading this article https://redux.js.org/basics/actions

like image 2
euvs Avatar answered Oct 17 '22 19:10

euvs