我一直在尝试将Redux-Saga集成到我的React / Redux应用程序中一段时间,并且我缺少一些关键部分(因为它不想工作)。
我对这应该如何工作的理解如下:
rootSaga
函数中,我分出了一个“Watcher”,它监视着一个特定的动作。put
或call
为观察者发送一个动作来查看。下面是我最接近(概念上)让它工作(代码匆忙匿名,所以请原谅任何错别字)。
我的sagas.ts文件:
import { all, call, fork, put, takeLatest} from 'redux-saga/effects';
import { fromJS } from 'immutable';
import AppRedux, { IAction } from 'app/containers/App/reducers';
const fetchData = async (id: string, name: string): Promise<any> => {
console.log('calling fetchData');
const resource = `someurl?id=${id}`;
const data = await((await fetch(resource)).json()); // request(resource);
console.log('returning fetchData');
return fromJS({
id: id,
name,
data,
});
};
const callFetchData = function* callFetchData(action: IAction) {
console.log('calling callFetchData*');
try {
const result = yield call(fetchData, action.id, action.name);
yield put({
type: AppRedux.Action.DATA_FETCHED,
result,
});
} catch (error) {
yield put({
type: AppRedux.Action.FETCH_FAILED,
error,
});
}
console.log('exiting callFetchData*');
};
const watchCallFetchData = function* watchCallFetchData(action: IAction): IterableIterator<any> {
console.log('calling watchCallFetchData*');
yield* takeLatest(AppRedux.Action.FETCH_DATA, callFetchData, action)[Symbol.iterator];
console.log('exiting watchCallFetchData*');
};
export function* rootSaga(action: IAction): IterableIterator<any> {
console.log('calling rootSaga*');
const watcher = yield all([
fork(watchCallFetchData, action),
]);
console.log('exiting rootSaga*');
}
export default [
rootSaga,
];
我的routes.ts文件:
import { RouterState } from 'react-router';
import { ComponentCallback, errorLoading, loadModule } from 'app/routes';
import AppRedux from 'app/containers/App/reducers';
import { call, put } from 'redux-saga/effects';
path: '/somepath/:id',
async getComponent(nextState: RouterState, cb: ComponentCallback) {
try {
const renderRoute = loadModule(cb);
const [reducer, sagas, component] = await importModules();
const id = nextState.params.id;
const name = '';
const action = {
id,
name,
type: AppRedux.Action.FETCH_DATA,
};
console.log('routes.ts pre-put fetch_data');
const putResult = put(action);
console.log('routes.ts post-put fetch_data');
return renderRoute(component);
} catch (err) {
errorLoading(cb)(err);
}
},
我的app.tsx文件:
import * as React from 'react';
import * as ReactRouter from 'react-router';
import { connect, DispatchProp } from 'react-redux';
import AppRedux from 'app/containers/App/reducers';
import { createStructuredSelector } from 'reselect';
import { selectid, selectResponses, selectname } from 'app/containers/App/selectors';
import { ISection } from './data';
export class App extends React.Component<IProps, {}> {
constructor(props: Readonly<IProps>) {
super(props);
console.log('app-constructor');
}
public render() {
console.log('app-render');
return (<div className="app" />);
}
}
export default connect<{}, {}, IProps>(
createStructuredSelector({
id: selectId(),
data: selectData(),
name: selectName(),
}),
(dispatch) => ({
fetchData: (id: string, name: string) => dispatch(AppRedux.fetchData(id, name)),
dispatch,
}),
)(App);
以下是console.log
调用的输出:
calling rootSaga*
calling watchCallFetchData*
exiting watchCallFetchData*
exiting rootSaga*
routes.ts pre-put fetch_data
routes.ts post-put fetch_data
app-constructor
app-render
编辑:澄清
我期待发生的事情:
put(action)
能够发布传奇动作并让Redux-Saga真正做点什么。callFetchData
,并且同样地调用它来调用fetchData
。exiting watchCallFetchData*
中的console.log
之前,这两个调用都会发生。实际发生了什么:
rootSaga*
函数。watchCallFetchData*
被召唤两次? (假设基于yield
语句应该做什么)。rootSaga*
函数再次被调用。 (假设基于yield
语句应该做什么)。我可以看到2个问题:
1.使用yield
而不是yield*
:你应该使用没有yield
(超级巨星)的*
:
yield* takeLatest(AppRedux.Action.FETCH_DATA,...
yield*
是一个扩展运算符,它简单地接受子程序中的所有调用,并将它们扩展为宏函数yield* docs。它不与takeLatest
一起使用。
2.不要将action
作为参数传递:action参数自动传递,因此它应该只是:
yield* takeLatest(AppRedux.Action.FETCH_DATA, callFetchData)
由于action
未传递给rootSaga(),因此参数将为undefined
。当takeLatest
尝试自动将action
参数传递给callFetchData时,它会将它附加到已存在的参数列表(see the Notes section in takeLatest docs)然后callFetchData将获得实际操作作为第二个参数,该函数不期望,因此它赢了不会过去的。
一个建议:yield all()
内部并行运行所有调用,所以我们应该在其中使用call
而不是fork
:
const watcher = yield all([
call(watchCallFetchData, action),
]);
takeLatest()
和while(take())
之间的区别:while循环中的take
是同步和阻塞的,所以它不会移动到下一行,直到它看到调度了所需的动作。它暂停整个传奇。然而,takeLatest()
分叉异步非阻塞线程等待所需的操作,并继续执行传奇的下一行。
@Ali Saeed建议的变化对我来说是正确的,但我认为主要问题是你在put
中使用routes.ts
。
只能在redux-saga运行的生成器(saga)中运行。
在您的routes.ts文件中
const putResult = put(action);
那条线什么也没做。如果从中查看返回值,您将看到它只返回一个对象。
你想做
store.dispatch(action);
根据您的代码,商店似乎不可用,因此您也必须更新它。