【问题标题】:Type Safe Redux Reducer with Arguments输入带参数的安全 Redux Reducer
【发布时间】:2020-10-15 02:06:29
【问题描述】:

我正在尝试将 typescript 与 react-redux 一起使用,但我在使用 reducer 时遇到了一些问题。使用当前设置,我遇到了错误,但我不知道为什么会这样。如果我偏离了 redux 文档中的方法,我可以得到一些有效但重复的东西。

-- 代码:-----

我在三个单独的目录中有三个文件,类似于:

store
|_actions
|   |_auth.ts  
|_reducers
|   |_auth.ts
|_types
    |_actionTypes.ts
    |_auth.ts

actions/auth.ts 文件内:

// actions/auth.ts
import { AuthActionTypes } from '../types/auth';
import * as actionTypes from '../types/actionTypes';

export const authStart = (): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_START
    };
};

export const authSuccess = ( token: string, userId: string ): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_SUCCESS,
        idToken: token,
        userId: userId
    };
};

export const authFail = ( error: Error ): AuthActionTypes => {
    return {
        type: actionTypes.AUTH_FAIL,
        error: error
    };
};

reducers/auth.ts 文件:

// reducers/auth.ts
import * as actionTypes from '../types/actionTypes';
import { AuthState, AuthActionTypes } from '../types/auth';
import { updateObject } from '../utility';

const initialState: AuthState = {
    token: null,
    userId: null,
    error: null,
    loading: false,
    authRedirectPath: '/'
};

const authStart = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, {error: null, loading: true })
};

const authSuccess = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};

const authFail = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, {
        error: action.error, // ##### this is giving me an error #####
        loading: false
    });
};

const reducer = (state = initialState, action: AuthActionTypes): AuthState => {
    switch (action.type) {
        case actionTypes.AUTH_START: return authStart( state, action );
        case actionTypes.AUTH_SUCCESS: return authSuccess( state, action );
        case actionTypes.AUTH_FAIL: return authFail( state, action );
        default: 
            return state;
    };
};

export default reducer;

types/auth.ts 文件:

// types/auth.ts
import * as actionTypes from './actionTypes';

export interface AuthState {
    token: null | string
    userId: null | string
    error: null | Error
    loading: boolean
    authRedirectPath: string
} 

interface AuthStart {
    type: typeof actionTypes.AUTH_START
}

interface AuthSuccess {
    type: typeof actionTypes.AUTH_SUCCESS;
    idToken: string;
    userId: string;
}

interface AuthFail {
    type: typeof actionTypes.AUTH_FAIL;
    error: Error;
}

export type AuthActionTypes = 
| AuthStart 
| AuthSuccess
| AuthFail;

为了完整性,utility.ts 文件中的updateObject 函数在reducers/auth.ts 中使用

export const updateObject = <T, U>(oldObject: T, updatedProperties: U): T => {
    return {
        ...oldObject,
        ...updatedProperties
    };
};

----- 问题:-----

reducers/auth.ts 中,问题在于authSuccess 箭头函数。我收到以下错误消息:

Property 'idToken' does not exist on type 'AuthActionTypes'.
Property 'idToken' does not exist on type 'AuthStart'.

我们可以看到idTokenuserId 都在interface 中定义。我不明白为什么它会引用AuthStart 而不是AuthSuccess。如果我导出 AuthSucess 接口并将其声明为该函数的操作类型,则错误消息将消失。有什么方法可以将 action 保持为 AuthActionType 还是需要明确声明为 AuthSucess 并且对于其他设置也一样?

另外,如果我删除所有箭头函数并在错误不再存在的情况下直接定义逻辑。那就是如果我有

// reducers/auth.ts
...
const reducer = (state = initialState, action: AuthActionTypes): AuthState => {
    switch (action.type) {
        ...
        case actionTypes.AUTH_SUCCESS: 
            return { 
               ...state, 
               token: action.idToken, 
               userId: action.userId, 
               error: null,
             };
...

有没有什么方法可以保持我现在的设置而不必直接在 swtich 案例中使用上述逻辑,因为我拥有的解决方案更容易维护?

【问题讨论】:

  • 清楚地告诉你,你没有像 idToken 这样的属性的接口

标签: reactjs typescript redux


【解决方案1】:

单独考虑AuthSuccess函数的定义:

const authSuccess = ( state: AuthState, action: AuthActionTypes ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};

此定义表明action 可以是任何AuthActionTypes,包括缺少idToken 属性的AuthStart。而是将函数键入为

const authSuccess = ( state: AuthState, action: AuthSuccess ) => {
    return updateObject( state, { 
        token: action.idToken, // ##### this is giving me an error #####
        userId: action.userId, // ##### this is giving me an error #####
        error: null,
        loading: false
    });
};

reducer 中的 switch 语句将允许 typescript 将传递给 authSuccessaction 的类型细化为仅 AuthSuccess 而不是其他 AuthActionTypes 之一。

这就是内联这些函数可以正常工作的原因 - 编译器知道该操作必须是 case 块中的 AuthSuccess 类型,因此 idTokenuserId 是有效的。

【讨论】:

    【解决方案2】:

    除了接受的答案外,我们特别建议您改用our official Redux Toolkit package。它包括用于简化几个常见 Redux 用例的实用程序,包括存储设置、定义 reducer、不可变更新逻辑,甚至一次创建整个状态“切片”。它也已经用 TypeScript 编写了,并且可以最大限度地减少您必须编写的类型声明的数量。

    看看您的示例,使用 createSlice 可能会消除您那里的一半代码,并将其缩减为单个文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-07-10
      • 1970-01-01
      • 1970-01-01
      • 2012-07-24
      • 1970-01-01
      • 1970-01-01
      • 2016-10-23
      相关资源
      最近更新 更多