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;
Proposed solution:
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:
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.