How do you get the result from action in another action?

593 views Asked by At

I am new to dvajs. I am using React 17,x, Typescript 4.2.3, antd 4.15.0, umi 3.4.7

The problem. If I navigate to the /s/ page, the code should get some categories from the API and make another request to the server, displaying them on some pages at this time. But the moment the search effect is called, the categories are empty, so I try to call the getCategories action again. But the categories are still empty. How do you get the result from action in another action? Maybe there is some kind of dispatch chain?

The code is provided below

import { fetchCategories, makeSearch } from '@/services/search';
import { CategoriesModelStateSelector, Subscription } from '@@/plugin-dva/connect';
import { Reducer, Effect } from 'umi';
export type SearchModelState = {
  searchResults: Provider[]
  categories: Category[]
}
export type SearchModelType = {
  namespace: string;
  state: SearchModelState;
  effects: {
    getCategories: Effect;
    search: Effect;
  };
  reducers: {
    setCategories: Reducer
    setSearchResults: Reducer
  };
  subscriptions: {
    setup: Subscription
  }
};
export type SearchModelStateSelector = {
  search: SearchModelState
};
const SearchModel: SearchModelType = {
  namespace: 'search',
  state: {
categories: [],
searchResults: [],
},      
effects: {
    * getCategories({ payload }, { call, put, select }) {
      const response = yield call(fetchCategories);
  if (response) {
    yield put({
      type: 'setCategories',
      payload: response,
    });
  }
},
* search({ payload }, { call, put, select }) {
  let categories = yield select(({ categories: state }: CategoriesModelStateSelector) => state.categories);
  // HERE categories are empty
  console.log(categories);
  yield put({
    type: 'getCategories',
    payload: {},
  });

  let catss = yield select(({ categories: state }: CategoriesModelStateSelector) => state.categories);
  console.log(catss);
  // HERE categories are still empty

  const response = yield call(makeSearch, payload);
  yield put({
    type: 'setSearchResults',
    payload: response,
  });
},
},


    reducers: {
        setCategories(state, { payload }) {
          return {
            ...state,
            categories: response,
      };
    },
    setSearchResults(state, { payload }) {
      return {
        ...state,
        searchResults: response,
      };
    },
  },
  subscriptions: {
    setup({ dispatch, history }) {
      // @ts-ignore
      return history.listen(({ pathname, query }) => {
        dispatch({ type: 'getCategories', payload: {} });

        if (pathname === '/s' || pathname === '/s/') {
          dispatch({ type: 'search', payload: query });
        }
      });
    },
  },
};

export default SearchModel;
1

There are 1 answers

0
Stefan Majiros On BEST ANSWER

Proposed solution:

yield put({
    type: 'getCategories',
    payload: {},
  });

// something like this will wait until CATEGORIES_WERE_FETCHED was dispatched on 
// the store, before continuing to next line: 'take' is what you are looking for
yield take("CATEGORIES_WERE_FETCHED")

let catss = yield select(

Redux-Saga Take: Creates an Effect description that instructs the middleware to wait for a specified action on the Store. The Generator is suspended until an action that matches pattern is dispatched.

https://redux-saga.js.org/docs/api/

I took another look at your code:

yield put({
      type: 'setCategories',
      payload: response,
    });

You are using 'setCategories' action, so you could use take('setCategories') - if you want to 'wait' until some action was dispatched, just in a way above.