我正在使用Redux,Saga和使用Typescript在React中开发应用程序。
应用程序结构设置为每个主要顶级容器组件在Store目录中都有一个相应的文件,用于定义它的动作创建者,减少者和传奇。
当应用程序启动时,所有Reducer都是从Store文件组合而来的,并且Sagas被组合成一个共同的rootSaga函数。
一切正常,但现在我正在尝试使用选择器将一些状态属性加载到我的一个Sagas中。我没有收到任何错误,但我的选择器功能没有返回我的状态值。
如果我尝试在Store文件中使用getState()函数,我会收到一个Typescript错误'找不到名称getState'。
显然,我没有在我的Store文件中包含正确的库,或者我没有通过正确的命名空间调用状态函数,但我无法弄清楚是什么问题。
我从Thunk中间件切换到使用Saga。当Thunk连接到应用程序时,我可以在Store文件中使用getState。
这是包含我的动作创建者,减少者和传奇的商店文件。
我的选择器功能也在文件中(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'。
知道我做错了吗?
答案 0 :(得分:1)
select
的参数是回调:
const callback = state => state.asVersionQueue.versionQueueFilter
const versionQueueFilters = yield select(callback)
答案 1 :(得分:0)
看起来我误解了传奇选择是如何运作的。
在我的职能中:
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;
我传入我的选择的函数没有提供任何东西,但是将它留空会返回整个状态树。