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

abd*_*hab 4 typescript reactjs react-redux redux-toolkit

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

例如

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

function* exampleSaga(action: ?) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

我怎么能说exampleSagaaction是类型getData



要了解我是如何尝试这样做的,请参考以下(示例)代码并查看我面临的问题的评论:

减速机.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>
>;
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

abd*_*hab 5

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

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

常见的.ts

import { createAction } from '@reduxjs/toolkit';

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

Run Code Online (Sandbox Code Playgroud)

getData 如果给定正确的有效负载,则已经煮熟以从 FunctionComponent 分派:

组件.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;

Run Code Online (Sandbox Code Playgroud)

与传奇

有一个传奇听动作

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
}
Run Code Online (Sandbox Code Playgroud)

将动作指定为 saga 的参数。

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

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));
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

This is how you will get it using return type of your getData.

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

...

}
Run Code Online (Sandbox Code Playgroud)