Search code examples
javascriptreactjsreduxredux-thunk

Calling one redux action from one store slice in another store slice


I am new to redux. In my app i am using some async actions with redux thunk. For example i have this action for loading movies from API:

export const loadMovies = (url) => async (dispatch) => {
  const response = await fetch(url);
  const result = await response.json();
  const { total_pages: totalPages, results: movies } = result;
  dispatch(moviesLoaded(totalPages, movies));
};

And i have situationL what if there are no movies to load (search in database didnt give me results) so i want to update status (it is the other redux store slice) to 'no movies' for example. And based on that status render different component.

So my new action would be something like this:

    export const loadMovies = (url) => async (dispatch) => {
      const response = await fetch(url);
      const result = await response.json();
      if (result) {
      const { total_pages: totalPages, results: movies } = result;
      dispatch(moviesLoaded(totalPages, movies));
      } else {
       dispatch(updateStatus('no-movies')) // updateStatus is imported from another redux store slice
      }
    };

I am wondering is it ok to do so. Or it is a bad practice to import actions from one store slice into another. And what is a better way to handle this situatuion.


Solution

  • You don't need to import actions from another store slice. You can handle the same query movies action type in multiple store slices' reducer. For more info, see Allow Many Reducers to Respond to the Same Action

    E.g.

    import { createStore, applyMiddleware, combineReducers } from 'redux';
    import thunk from 'redux-thunk';
    
    const GET_MOVIES_FULFILLED = 'GET_MOVIES_FULFILLED';
    
    function moviesLoaded(totalPages, movies) {
      return {
        type: GET_MOVIES_FULFILLED,
        payload: {
          totalPages,
          movies,
        },
      };
    }
    
    export const loadMovies = () => async (dispatch) => {
      //   const response = await fetch(url);
      //   const result = await response.json();
      // mock result
      const result = { total_pages: 0, results: [] };
      const { total_pages: totalPages, results: movies } = result;
      dispatch(moviesLoaded(totalPages, movies));
    };
    
    const rootReducer = combineReducers({
      sliceA(state = { totalPages: 0, movies: [] }, action) {
        switch (action.type) {
          case GET_MOVIES_FULFILLED:
            return action.payload;
          default:
            return state;
        }
      },
    
      sliceB(state = { status: '' }, action) {
        switch (action.type) {
          case GET_MOVIES_FULFILLED:
            if (!action.payload.movies || !action.payload.movies.length) {
              return {
                status: 'no movies',
              };
            }
          default:
            return state;
        }
      },
    });
    
    const store = createStore(rootReducer, applyMiddleware(thunk));
    
    store.dispatch(loadMovies() as any).then(() => {
      console.log(store.getState());
    });
    

    Output:

    {
      sliceA: { totalPages: 0, movies: [] },
      sliceB: { status: 'no movies' }
    }
    

    Dispatch the GET_MOVIES_FULFILLED action, handle it in sliceA and sliceB reducers.

    If there are no movies, we set the state of sliceB to {status: 'no movies'}.