Redux-Saga我错过了什么?

问题描述 投票:0回答:2

我一直在尝试将Redux-Saga集成到我的React / Redux应用程序中一段时间​​,并且我缺少一些关键部分(因为它不想工作)。

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

  1. rootSaga函数中,我分出了一个“Watcher”,它监视着一个特定的动作。
  2. 当观察者看到该动作时,它会调用该动作的处理程序并在完成时调用适当的成功/失败动作。
  3. 我应该使用putcall为观察者发送一个动作来查看。

下面是我最接近(概念上)让它工作(代码匆忙匿名,所以请原谅任何错别字)。

我的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. 我希望(在某些时候)有一个“A-ha!”时刻。

实际发生了什么:

  1. 调用rootSaga*函数。
  2. watchCallFetchData*被召唤两次? (假设基于yield语句应该做什么)。
  3. rootSaga*函数再次被调用。 (假设基于yield语句应该做什么)。
  4. 一个“A-ha!”那一刻继续躲避我。
typescript redux react-router redux-saga
2个回答
1
投票

我可以看到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()分叉异步非阻塞线程等待所需的操作,并继续执行传奇的下一行。


0
投票

@Ali Saeed建议的变化对我来说是正确的,但我认为主要问题是你在put中使用routes.ts

只能在redux-saga运行的生成器(saga)中运行。

在您的routes.ts文件中

const putResult = put(action);

那条线什么也没做。如果从中查看返回值,您将看到它只返回一个对象。

你想做

store.dispatch(action);

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

© www.soinside.com 2019 - 2024. All rights reserved.