【问题标题】:Redux Toolkit: How to refer action created with createAction as typeRedux Toolkit:如何将使用 createAction 创建的操作作为类型引用
【发布时间】: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);
}

【问题讨论】:

    标签: reactjs typescript react-redux redux-toolkit


    【解决方案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));
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-21
        • 2021-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-04
        • 2021-07-09
        • 2022-10-13
        相关资源
        最近更新 更多