Reactjs 带参数的类型安全Redux Reducer
我正在尝试将typescript与react redux一起使用,我在reducer方面遇到了一些问题。在当前的设置中,我会出现错误,但我不明白为什么会出现这种情况。如果我偏离redux文档中的方法,我可以得到一些有用的东西,但它是重复的 ----代码:----- 我在三个不同的目录中有三个文件,类似于:Reactjs 带参数的类型安全Redux Reducer,reactjs,typescript,redux,Reactjs,Typescript,Redux,我正在尝试将typescript与react redux一起使用,我在reducer方面遇到了一些问题。在当前的设置中,我会出现错误,但我不明白为什么会出现这种情况。如果我偏离redux文档中的方法,我可以得到一些有用的东西,但它是重复的 ----代码:----- 我在三个不同的目录中有三个文件,类似于: store |_actions | |_auth.ts |_reducers | |_auth.ts |_types |_actionTypes.ts |_auth
store
|_actions
| |_auth.ts
|_reducers
| |_auth.ts
|_types
|_actionTypes.ts
|_auth.ts
在操作/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
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
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;
reducers/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
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
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;
类型/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
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
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;
为完整起见,reducers/auth.ts中使用的utility.ts
文件中的updateObject
函数
export const updateObject = <T, U>(oldObject: T, updatedProperties: U): T => {
return {
...oldObject,
...updatedProperties
};
};
我们可以看到idToken
和userId
都是在界面中定义的。我不明白为什么它会引用AuthStart
,而不是authsucture
。如果我导出AuthSucess
接口并将其声明为该函数的操作类型,则错误消息将消失。是否有任何方法将操作保持为AuthActionType
,或者是否需要显式声明为AuthSuces
,并且对于使用此设置的其他操作也是如此
另外,如果我删除了所有的箭头函数,并在错误不再存在的情况下直接定义了逻辑。如果我有
// 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案例中使用上述逻辑?考虑一下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
可以是任何authactionType
,包括缺少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中的开关
语句将允许typescript将传递给authSuccess
的操作
的类型细化为仅authSuccess
而不是其他AuthActionTypes
这就是内联这些函数工作正常的原因-编译器知道在情况下authsucture
块中的操作必须是authsucture
类型,因此idToken
和userId
是有效的。考虑authsucture
函数本身的定义:
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
可以是任何authactionType
,包括缺少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中的开关
语句将允许typescript将传递给authSuccess
的操作
的类型细化为仅authSuccess
而不是其他AuthActionTypes
这就是为什么内联这些函数可以很好地工作的原因-编译器知道在案例中的操作必须是authsucture
类型,因此idToken
和userId
是有效的。除了接受的答案之外,我们特别建议您切换到使用。它包括一些实用程序来简化几个常见的Redux用例,包括存储设置、定义缩减器、不可变的更新逻辑,甚至一次创建整个状态“切片”。它也已经用TypeScript编写,并且将最小化您必须编写的类型声明的数量
看看您的示例,使用createSlice
可能会删除您已有的一半代码,并将其缩减为一个文件。除了公认的答案之外,我们特别建议您切换到使用。它包括一些实用程序来简化几个常见的Redux用例,包括存储设置、定义缩减器、不可变的更新逻辑,甚至一次创建整个状态“切片”。它也已经用TypeScript编写,并且将最小化您必须编写的类型声明的数量
看看您的示例,使用createSlice
可能会删除一半的代码,并将其缩减为一个文件。清楚地告诉我们,您没有像idToken这样的属性接口清楚地告诉我们,您没有像idToken这样的属性接口