本文介绍了我在 Redux-Saga 中缺少什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一段时间以来,我一直在尝试将 Redux-Saga 集成到我的 React/Redux 应用程序中,但我遗漏了其中的一些关键部分(因为它不想工作).

I've been trying to integrate Redux-Saga into my React/Redux app for a while now and there is some key part of this that I'm missing (as it doesn't want to work).

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

My understanding of how this is supposed to work is as follows:

  1. rootSaga 函数中,我分出一个观察者",它监视特定动作的发生.
  2. 当观察者看到操作时,它会调用该操作的处理程序,并在完成时调用相应的成功/失败操作.
  3. 我应该使用 putcall 来分派一个动作让观察者看到.
  1. In the rootSaga function, I fork off a "Watcher" which watches for a particular action to occur.
  2. When the watcher sees the action, it calls the handler for that action and calls the appropriate success/fail action when completed.
  3. I should use put or call to dispatch an action for the watcher to see.

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

Below is the closest I've come (conceptually) to getting it to work (code hastily anonymized, so please forgive any typos).

我的 sagas.ts 文件:

My sagas.ts file:

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 文件:

My routes.ts file:

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 文件:

My app.tsx file:

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) 分派 saga 动作,并让 Redux-Saga 真正做一些事情.
  2. 我希望函数 callFetchData 被调用,同样地,它会调用 fetchData.
  3. 我希望在我看到 console.log 中的 exiting watchCallFetchData* 之前发生这两个调用.
  4. 我希望(在某个时候)会有啊哈!"时刻.
  1. I expect put(action) to dispatch the saga action and for Redux-Saga to actually do something.
  2. I expect the function callFetchData to be called, and like-wise, for it to call fetchData.
  3. I expect both of those calls to occur before I see exiting watchCallFetchData* in the console.log.
  4. I expect (at some point) to have an "A-ha!" moment.

实际发生的事情:

  1. rootSaga* 函数被调用.
  2. watchCallFetchData* 被调用两次?(基于 yield 语句应该做什么的假设).
  3. 再次调用 rootSaga* 函数.(基于 yield 语句应该做什么的假设).
  4. 一声啊哈!"片刻继续躲避我.
  1. The rootSaga* function gets called.
  2. The watchCallFetchData* gets called twice? (Assumption based on what the yield statements are supposed to do).
  3. The rootSaga* function gets called again. (Assumption based on what the yield statements are supposed to do).
  4. An "A-ha!" moment continues to elude me.

推荐答案

我可以看到 2 个问题:

I can see 2 issues:

1.使用 yield 而不是 yield*: 你应该使用 yield 而没有 *(超级明星)在:

1. Use yield instead of yield*: You should be using yield without the * (superstar) in:

yield* takeLatest(AppRedux.Action.FETCH_DATA,...

yield* 是一个扩展运算符,它只接收内部的所有调用一个子程序并将它们扩展为宏函数 yield* docs.它没有被使用使用 takeLatest.

yield* is a spread operator that simply takes all the calls inside a subroutine and expands them into the macro-function yield* docs. It's not used with takeLatest.

2.不要将 action 作为参数传递: action 参数是自动传递的,所以它应该是:

2. Don't pass action as parameter: The action parameter is passed automatically, so it should just be:

yield* takeLatest(AppRedux.Action.FETCH_DATA, callFetchData)

由于 action 未传递给 rootSaga(),因此参数将为 undefined.当 takeLatest 尝试自动将 action 参数传递给 callFetchData 时,它会将其附加到现有的参数列表(参见 takeLatest 文档中的注释部分) &然后 callFetchData 将获得真正的动作作为第二个参数,这是函数不期望的,所以它不会被传递.

As action is not passed to rootSaga() the argument would be undefined. When takeLatest will try to automatically pass the action parameter to callFetchData, it will append it to already existing list of arguments (see the Notes section in takeLatest docs) & then callFetchData will get the real action as the 2nd parameter, which the function isn't expecting, so it won't get passed.

建议: yield all() 在内部并行运行所有调用,因此我们应该使用 call 而不是 fork 在里面:

A suggestion: yield all() internally runs all calls in parallel, so we should use call instead of fork inside it:

const watcher = yield all([
    call(watchCallFetchData, action),
]);

takeLatest() & 的区别while(take()): while 循环中的 take 是同步和阻塞的,所以它不会移动到下一行,直到它看到一个发送所需的操作.它暂停了整个传奇.而 takeLatest() fork 一个异步非阻塞线程以等待所需的操作,&继续执行传奇的下几行.

Difference between takeLatest() & while(take()): the take in while loop is synchronous and blocking, so it doesn't move to the next line until it sees that a desired action is dispatched. It pauses the whole saga. Whereas, takeLatest() forks an asynchronous non-blocking thread to wait for the desired action, & keeps executing the next lines of the saga.

这篇关于我在 Redux-Saga 中缺少什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-16 07:11