为啥这个 Redux Saga Selector 不工作?我似乎无法访问状态

Posted

技术标签:

【中文标题】为啥这个 Redux Saga Selector 不工作?我似乎无法访问状态【英文标题】:Why isn't this Redux Saga Selector working? I can't seem to access state为什么这个 Redux Saga Selector 不工作?我似乎无法访问状态 【发布时间】:2018-06-29 06:17:45 【问题描述】:

我正在使用 Redux、Saga 和 Typescript 开发一个 React 应用程序。

应用程序结构的设置使得每个主要***容器组件在 Store 目录中都有一个相应的文件,用于定义它的动作创建者、reducers 和 sagas。

当应用启动时,所有的 reducer 都从 Store 文件中组合起来,而 Sagas 组合成一个通用的 rootSaga 函数。

除了现在我尝试使用选择器将一些状态属性加载到我的 Sagas 中之外,一切都很好。我没有收到任何错误,但我的选择器函数没有返回我的状态值。

如果我尝试在我的 Store 文件中使用 getState() 函数,我会收到 Typescript 错误“找不到名称 getState”。

很明显,我没有在我的 Store 文件中包含正确的库,或者我没有通过正确的命名空间调用 state 函数,但我不知道出了什么问题。

我从 Thunk 中间件切换到使用 Saga。当 Thunk 连接到应用程序时,我可以在 Store 文件中使用 getState。

这是包含我的 action creators、reducers 和 sagas 的 Store 文件。

我的选择器函数也在文件中(export const getVersionQueueFilters):

import  fetch, addTask  from 'domain-task';
import  Action, Reducer, ActionCreator  from 'redux';
import  takeLatest, takeEvery  from "redux-saga"
import  call, put, take, race, select  from "redux-saga/effects"
import * as moment from 'moment';

// -----------------
// STATE - This defines the type of data maintained in the Redux store.

export interface ASVersionQueueState 
    queuedVersions: QueuedVersion[];
    versionQueueFilter: VersionQueueFilter;
    eventsFilterList: SelectListItem[];
    employeesFilterList: SelectListItem[];
    gridIsLoading: boolean;
    versionQueueRefresh: boolean;
    error: boolean;


export interface QueuedVersion 
    VersionCode: string;
    VersionQualifier: string;
    VersionID: string;
    ProductID: string;
    PieceName: string;
    PrintClass: string;
    FirstInhomeDate: string;
    AccountID: string;
    AccountExecutive: string;
    AccountManager: string;
    ArtManager: string;
    AdUID: string;
    Status: string;
    Queue: string;
    DueDateOverride: string;
    IsLocked: string;


export interface VersionQueueFilter 
    StartDate: string;
    EndDate: string;
    PieceType: Array<string>;
    EventType: Array<string>;
    EventID: string;
    Employee: string;


export interface SelectListItem 
    OptionName: string;
    OptionVal: string;


export let DefaultVersionQueueFilter = 
    StartDate: moment().subtract(30, 'days').format('YYYY-MM-DD'),
    EndDate: moment().format('YYYY-MM-DD'),
    PieceType: ['impactpc'],
    EventType: ['special'],
    EventID: '',
    Employee: '12345'


// Version Queue polling delay value
let versionQueuePollDelay: number = 10000;  // Delay in milliseconds

// -----------------
// ACTIONS - These are serializable (hence replayable) descriptions of state transitions.
// They do not themselves have any side-effects; they just describe something that is going to happen.
// Use @typeName and isActionType for type detection that works even after serialization/deserialization.

interface PollVersionsAction 
    type: 'POLL_VERSIONS';
    versionQueueFilter: VersionQueueFilter;
    versionQueueRefresh: boolean;


interface PollRequestVersionsAction 
    type: 'POLL_REQUEST_VERSIONS';
    versionQueueFilter: VersionQueueFilter;
    versionQueueRefresh: boolean;


interface PollRequestVersionsSuccessAction 
    type: 'POLL_REQUEST_VERSIONS_SUCCESS';
    versionQueueFilter: VersionQueueFilter;
    receivedVersions: QueuedVersion[];
    versionQueueRefresh: boolean;


interface PollRequestVersionsErrorAction 
    type: 'POLL_REQUEST_VERSIONS_ERROR';



// Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the
// declared type strings (and not any other arbitrary string).
type KnownAction = PollVersionsAction | PollRequestVersionsSuccessAction | PollRequestVersionsErrorAction;

// ----------------
// ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition.
// They don't directly mutate state

export const actionCreators = 

    pollVersions: () => 
        return  type: 'POLL_VERSIONS', versionQueueFilter: getVersionQueueFilters, versionQueueRefresh: true 
    ,
    pollRequestVersions: (versionQueueFilter: VersionQueueFilter, versionQueueRefresh: boolean) => 
        return  type: 'POLL_REQUEST_VERSIONS', versionQueueFilter: versionQueueFilter, versionQueueRefresh: versionQueueRefresh 
    ,
    pollRequestVersionsSuccess: (versionQueueFilter: VersionQueueFilter, versionQueueRefresh: boolean, data: QueuedVersion[]) => 
        return  type: 'POLL_REQUEST_VERSIONS_SUCCESS', versionQueueFilter: versionQueueFilter, receivedVersions: data, versionQueueRefresh: versionQueueRefresh 
    ,
    pollRequestVersionsError: () => 
        return  type: 'POLL_REQUEST_VERSIONS_ERROR' 
    
;

// ----------------
// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state.
const unloadedState: ASVersionQueueState =  gridIsLoading: false, versionQueueRefresh: false, queuedVersions: [], versionQueueFilter: DefaultVersionQueueFilter, eventsFilterList: [], employeesFilterList: [], error: false ;

export const reducer: Reducer<ASVersionQueueState> = (state: ASVersionQueueState, incomingAction: Action) => 
    const action = incomingAction as KnownAction;
    switch (action.type) 
        case 'POLL_VERSIONS':
            return 
                ...state,
                versionQueueFilter: action.versionQueueFilter,
                versionQueueRefresh: action.versionQueueRefresh,
                gridIsLoading: true
            
        case 'POLL_REQUEST_VERSIONS_SUCCESS':
            // Only accept the incoming data if it matches the most recent request. This ensures we correctly
            // handle out-of-order responses.
            if (action.versionQueueFilter === state.versionQueueFilter && action.versionQueueRefresh === state.versionQueueRefresh) 
                return 
                    ...state,
                    queuedVersions: action.receivedVersions,
                    versionQueueRefresh: action.versionQueueRefresh,
                    gridIsLoading: false
                
            
            break;
        case 'POLL_REQUEST_VERSIONS_ERROR':
            return 
                ...state,
                error: true
            
        default:
            // The following line guarantees that every action in the KnownAction union has been covered by a case above
            const exhaustiveCheck: never = action;
    

    return state || unloadedState;
;

// Sagas
// Saga Watchers
export const sagas = [
    takeEvery('POLL_VERSIONS', fetchPollVersionsAsync)
]

// Selector Function
export const getVersionQueueFilters = (store: ASVersionQueueState) => store.versionQueueFilter;

// Utility function to delay effects
export function delay(delayMS: number) 
    const promise = new Promise(resolve => 
        setTimeout(() => resolve(true), delayMS)
    );
    return promise;


export function* versionPoller() 
    const versionQueueFilters = yield select(getVersionQueueFilters);
    try 
        yield call(delay, versionQueuePollDelay);
        yield put(actionCreators.pollVersions() );
     catch (error) 
        // cancellation error
        return;
    


export function* watchVersionPoller() 
    while (true) 
        yield take('POLL_REQUEST_VERSIONS_SUCCESS');
        yield call(versionPoller);
    


export function* fetchPollVersionsAsync(action: PollVersionsAction) 
    try 
        yield put(actionCreators.pollRequestVersions(action.versionQueueFilter, action.versionQueueRefresh));
        const data = yield call(() => 
            return fetch('api/Versions')
                .then(res => res.json())
        
        );
        yield put(actionCreators.pollRequestVersionsSuccess(action.versionQueueFilter, action.versionQueueRefresh, data));
     catch (error) 
        yield put(actionCreators.pollRequestVersionsError());
    

选择器用于saga函数“versionPoller()”。

基本上,我会轮询我的 API 以获取任何更新的数据,但它至少需要传递一组默认的过滤器值。我想为此使用当前处于状态的过滤器值。

我也尝试将我的选择器函数定义为:

export const getVersionQueueFilters = getState().ASVersionQueueState.versionQueueFilter;

当我这样做时,我收到错误“找不到名称 getState”。

知道我做错了什么吗?

【问题讨论】:

【参考方案1】:

select的参数是回调:

const callback = state => state.asVersionQueue.versionQueueFilter
const versionQueueFilters = yield select(callback)

【讨论】:

【参考方案2】:

看来我误解了传奇选择的工作原理。

在我的功能中:

export function* versionPoller() 
    const versionQueueFilters = yield select(getVersionQueueFilters);
    try 
        yield call(delay, versionQueuePollDelay);
        yield put(actionCreators.pollVersions() );
     catch (error) 
        // cancellation error
        return;
    

我应该做的是:

const state = yield select();
const versionQueueFilters = state.asVersionQueue.versionQueueFilter;

我传递给我的选择的函数没有提供任何东西,而是将其留空返回整个状态树。

【讨论】:

您是正确的,不带参数调用 select() 会返回整个状态树,但我认为这不是最初错误的原因。正如@dancerphil 回答的那样,您的选择器有一个错误,它应该访问store.asVersionQueue.versionQueueFilter,但您只是访问了store.asVersionQueueFilter

以上是关于为啥这个 Redux Saga Selector 不工作?我似乎无法访问状态的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 redux-thunk 或 redux-saga 进行 fetches?

为啥 redux-saga 使用 put 方法而不是 dispatch?

如何使用 redux-saga-test-plan 测试选择器功能

手写Redux-Saga源码

我的减速机应该处理@@ redux-saga-test-plan / INIT动作吗?

什么时候应该使用 Redux Saga 而不是 Redux Thunk,什么时候应该使用 Redux Thunk 而不是 Redux Saga?