Redux Toolkit:如何将使用 createAction 创建的操作作为类型引用

Posted

技术标签:

【中文标题】Redux Toolkit:如何将使用 createAction 创建的操作作为类型引用【英文标题】:Redux Toolkit: How to refer action created with createAction as type 【发布时间】:2020-06-08 04:03:15 【问题描述】:

Redux 工具包提供了createAction 辅助方法。我在引用使用 createAction 作为类型创建的操作时遇到问题。

例如

const getData = createAction<PayloadAction<number>>('user/getData')

function* exampleSaga(action: ?) 
   ...

我如何告诉 exampleSaga action 的类型是 getData


要查看我是如何尝试的,请参考以下(示例)代码并查看我面临的问题的 cmets:

reducer.ts

import  createAction, PayloadAction  from '@reduxjs/toolkit';

export const getData = createAction<PayloadAction<number>>('user/getData');

const userSlice = createSlice(
  name: userSliceName,
  initialState,
  reducers: 
    getUser(state: IUserState): void 
      state.loading = true;
      state.error = null;
    ,
    getUserSuccess(state: IUserState, action: PayloadAction< user: IUser >): void 
      const  user  = action.payload;
      state.user = user;
      state.loading = false;
      state.error = null;
    ,
    getUserFailure(state: IUserState, action: PayloadAction<string>): void 
      state.loading = false;
      state.error = action.payload;
    ,
  ,
);

export type UserSaga = Generator<
  | CallEffect<IUser>
  | PutEffect<typeof getUserSuccess>
  | PutEffect<typeof getUserFailure>
  | ForkEffect<never>
>;

sagas.ts

import  UserSaga, getData  from './reducer.ts';

// For the following fetchUser saga arguments, how can I define that 'action' is of type getData, imported from the above 'reducer.ts' file.
function* fetchUser(action: PayloadAction<typeof getData>): UserSaga 
  try 
    const userId = action.payload;
    // Problem is here
    // I expect userId to be of type number, whereas it is:
    // ActionCreatorWithPayload<payload: number, type: string>
    ...
   catch (e) 
    ...
  


function* userSaga(): UserSaga 
  const pattern: any = getData.type;
  yield takeLatest(pattern, fetchUser);

【问题讨论】:

【参考方案1】:

这是使用 getData 的返回类型获取它的方式。

function* exampleSaga(action: ReturnType<typeof getData>) 

...


【讨论】:

【参考方案2】:

我做了更多的挖掘,这就是我解决这个问题的方法。

可以在不将其绑定到 SliceCaseReducer 的情况下创建操作:(请注意,下面创建的 getData 本身不是 Action,而是 ActionCreator )。

common.ts

import  createAction  from '@reduxjs/toolkit';

export const getData = createAction<number>('getData');

getData 如果给定了正确的有效负载,则已经准备好从 FunctionComponent 分派:

component.tsx

import React,  useEffect  from 'react';
import  useDispatch  from 'react-redux';
import  useParams  from 'react-router';
import  getData  from './state/common';

const User: React.FC = (): React.ReactElement => 
  const  userId  = useParams();
  const dispatch = useDispatch();

  useEffect((): void => 
    dispatch(getData(Number(userId)));
  , []);

  return (<>User Component</>);
;

export default UserContainer;

与传奇

让传奇听着行动

Saga Effects 接受字符串类型的动作来做出反应。幸运的是,actionCreator 公开了一个 type 属性,表示操作的已定义字符串名称。

saga.ts(监听器)

import  takeLatest  from 'redux-saga/effects';
import  getData  from './common';

function* userSaga() 
  // getData.type could be input to a Saga Effect
  yield takeLatest(getData.type, fetchUser); // fetchUser is a worker saga explained below

将动作指定为 saga 的参数。

将动作指定为 saga 的参数有点棘手,我们必须使用 TypeScript 的 Type Guards。有趣的是,redux-toolkit 的documentation 中也提到了它。简而言之,我们必须使用 actionCreator 的actionCreator.match 方法来将传递的动作区分为所需的类型。因此,经过区分后,我们会收到匹配操作的有效负载所需的静态类型。

saga.ts(工人)

import  Action  from '@reduxjs/toolkit';
import  call, put  from 'redux-saga/effects';
import * as userApi from '../../../api/user-api';
import  getData  from './common';
import  getUserSuccess, getUserFailure  from './user';

// Note that we can't directly specify that fetchUser only accepts getData action type.
function* fetchUser(action: Action) 
  try 
    // getData.match could be used to assert that we are only concerned with getData action type
    if (getData.match(action)) 
      // Inside the body of if, we get correct static typing
      const userId = action.payload;

      const fetchedUser = yield call(userApi.getUser, userId);
      yield put(getUserSuccess(user: fetchedUser));
    
   catch (e) 
    yield put(getUserFailure(e.message));
  

【讨论】:

以上是关于Redux Toolkit:如何将使用 createAction 创建的操作作为类型引用的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 redux-toolkit 动态更改基本 URL?

如何在 Storybook 中使用 redux-toolkit?

如何在 Redux-toolkit 中使用两个不同的切片?

Redux Toolkit RTK Query 发送查询参数

使用 @reduxjs/toolkit 中的 configureStore 时如何重置 Redux Store 的状态?

如何使用 redux-toolkit 中的 createSlice 方法在不同的 reducer 函数中使用单个动作类型