我已经尝试将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
语句应该做什么)。答案 0 :(得分:1)
我可以看到两个问题:
<强> 1。使用yield
代替yield*
:您应该使用yield
而不使用*
(超级明星):
yield* takeLatest(AppRedux.Action.FETCH_DATA,...
yield*
是一个传播运算符,它只需要调用所有调用
子例程并将它们扩展为宏函数yield* docs。它没用过
与takeLatest
。
<强> 2。不要将action
作为参数传递:动作参数会自动传递,因此它应该只是:
yield* takeLatest(AppRedux.Action.FETCH_DATA, callFetchData)
由于action
未传递给 rootSaga(),因此参数为undefined
。当takeLatest
尝试自动将action
参数传递给 callFetchData 时,它会将其附加到现有的参数列表(see the Notes section in takeLatest docs)&amp;然后 callFetchData 将获得实际操作作为第二个参数,该函数不期望,因此它不会被传递。
建议: yield all()
在内部并行运行所有调用,因此我们应该在其中使用call
而不是fork
:
const watcher = yield all([
call(watchCallFetchData, action),
]);
takeLatest()
&amp;之间的差异while(take())
: while循环中的take
是同步和阻塞的,因此在看到调度所需的操作之前,它不会移动到下一行。它暂停整个传奇。然而,takeLatest()
分叉异步非阻塞线程等待所需的操作,&amp;继续执行传奇的下一行。
答案 1 :(得分:0)
@Ali Saeed建议的更改对我来说是正确的,但我认为主要问题是您在put
内使用routes.ts
。
只能在redux-saga运行的生成器(saga)中使用。
在routes.ts文件中
const putResult = put(action);
那条线什么也没做。如果您查看返回值,您将看到它只返回一个对象。
你想做
store.dispatch(action);
根据您的代码,商店似乎不可用,因此您也必须更新它。