Redux-Saga我错过了什么?

时间:2017-12-19 15:35:47

标签: typescript redux react-router redux-saga

我已经尝试将Redux-Saga整合到我的React / Redux应用程序中一段时间​​了,而且有一些关键部分我不知道(因为它不想要工作)。

我对这应该如何运作的理解如下:

  1. rootSaga函数中,我分出一个" Watcher"它会监视特定的行为。
  2. 当观察者看到该动作时,它会调用该动作的处理程序,并在完成时调用相应的成功/失败动作。
  3. 我应该使用putcall来发送一个让观察者看到的动作。
  4. 以下是我最接近(概念上)使其正常工作(代码匆忙匿名,所以请原谅任何错别字)。

    我的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
    

    编辑:澄清

    我期待发生的事情:

    1. 我希望put(action)发送传奇动作,并让Redux-Saga真正做点什么。
    2. 我希望调用函数callFetchData,并且同样可以调用fetchData
    3. 我希望在exiting watchCallFetchData*中看到console.log之前,这两个电话都会发生。
    4. 我希望(在某些时候)有一个&#34; A-ha!&#34;时刻。
    5. 实际发生了什么:

      1. 调用rootSaga*函数。
      2. watchCallFetchData*被叫两次? (假设基于yield语句应该做什么)。
      3. 再次调用rootSaga*函数。 (假设基于yield语句应该做什么)。
      4. A&#34; A-ha!&#34;那一刻继续躲避我。

2 个答案:

答案 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);

根据您的代码,商店似乎不可用,因此您也必须更新它。