在我的 Redux 商店中,我正在初始化一个
persistedReducer
,它采用 redux-persist 配置和“rootReducer”:
client/src/redux/store.ts
:
import { configureStore } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/es/stateReconciler/autoMergeLevel2';
import rootReducer from './rootReducer'
const persistConfig = {
key: 'root',
storage,
stateReconciler: autoMergeLevel2,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export const store = configureStore({
reducer: persistedReducer
});
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
rootReducer 本身是这样声明和导出的:
client/src/redux/rootReducer.ts
:
import { combineReducers } from "@reduxjs/toolkit";
import { IUserState } from "../interfaces/user";
import userSlice from "./features/userSlice";
export interface RootState {
user: IUserState;
}
const rootReducer = combineReducers({
user: userSlice.reducer,
});
export default rootReducer;
我们导入并添加到 rootReducer 的
userSlice
减速器如下所示:
client/src/redux/features/userSlice.ts
:
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
import { IUserState } from "../../interfaces/user";
import { useSelector } from "react-redux";
import userApi from "../../apis/user";
import { RootState } from "../store";
const initialState: IUserState = {
_id: "",
ipAddress: "",
createdAt: "",
alignment: "",
loading: false,
error: "",
};
export const getUser = createAsyncThunk("user/getUser", async (_, thunkApi) => {
try {
const response = await userApi.getUser();
return response;
} catch (error) {
throw thunkApi.rejectWithValue({ error: "user not initialized" });
}
});
export const updateUser = createAsyncThunk<IUserState, any, { state: RootState }>(
"user/updateUser",
async (data: any, thunkApi) => {
console.log("document", data);
try {
const user = useSelector((state: RootState) => state.user);
const response = await userApi.updateUser(user, data);
return response;
} catch (error) {
throw thunkApi.rejectWithValue({ error: "user not updated" });
}
}
);
const userSlice = createSlice({
name: "userData",
initialState,
reducers: {
getUser: (state, action: PayloadAction<IUserState>) => {
state = Object.assign(state, action.payload);
return state;
},
updateUser: (state, action: PayloadAction<IUserState>) => {
state = Object.assign(state, action.payload);
return state;
},
},
extraReducers: (builder) => {
builder.addCase(getUser.fulfilled, (state, action) => {
state = Object.assign(state, action.payload);
state.loading = false;
state.error = "";
});
builder.addCase(getUser.pending, (state) => {
state.loading = true;
state.error = "";
});
builder.addCase(getUser.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message || "user not initialized";
});
},
});
export const { getUser: getUserAction, updateUser: updateUserAction } =
userSlice.actions;
export default userSlice;
用户状态界面是从
client/src/interfaces/user.ts
导入的,如下所示:
export type IUserState = {
_id: string
ipAddress: string
createdAt: string
alignment: string
loading: boolean
error: string
}
我在客户端输出状态中遇到的错误:
Argument of type 'Reducer<{ user: IUserState; }, UnknownAction, Partial<{ user: IUserState | undefined; }>>' is not assignable to parameter of type 'Reducer<unknown, UnknownAction, unknown>'.
Types of parameters 'state' and 'state' are incompatible.
Type 'unknown' is not assignable to type '{ user: IUserState; } | Partial<{ user: IUserState | undefined; }> | undefined'
我的
updateUser
asyncThunk 中的 Typescript 编译器有一个单独的错误,其中显示 Property 'user' does not exist on type 'PersistPartial'
。我不确定这是否与抱怨减速器类型不匹配的终端错误有关。将 rootReducer
转换为 any
显然可以使错误消失,但如果可能的话,我想避免这种情况。看来这可能是我定义 RootState 接口的方式的问题。
我认为问题在于你的
updateUser
,至少有几个问题。
createAsyncThunk
,因为它应该已经根据存储和有效负载创建者参数推断出正确的类型。updateUser
不是 React 组件或自定义 React hook,因此它不能使用 useSelector
钩子。使用 thunkApi.getState
访问当前状态值。rejectWithValue
应该从 Thunk 返回,而不是抛出。export const updateUser = createAsyncThunk<
IUserState,
any,
{
state: RootState,
rejectValue: {
error: string;
}
}
>("user/updateUser", (data: any, thunkApi) => {
try {
const user = thunkApi.getState().user;
return userApi.updateUser(user, data);
} catch (error) {
return thunkApi.rejectWithValue({ error: "user not updated" });
}
});
或
export const updateUser = createAsyncThunk(
"user/updateUser",
(data: any, thunkApi) => {
try {
const user = (thunkApi.getState() as RootState).user;
return userApi.updateUser(user, data);
} catch (error) {
return thunkApi.rejectWithValue({ error: "user not updated" });
}
}
);
此外,您在
userSlice
中遇到状态更新问题。您应该只改变状态或返回一个全新的状态参考值,并且您应该永远不重新分配状态。 state = Object.assign(state, payload)
在技术上可能有效,因为参考是相同的,但您不应该养成在 RTK 减速器中执行 state = ...
的习惯。
const userSlice = createSlice({
name: "userData",
initialState,
reducers: {
getUser: (state, action: PayloadAction<IUserState>) => {
// Just mutate state
Object.assign(state, action.payload);
// or return new state value
// return action.payload;
},
updateUser: (state, action: PayloadAction<IUserState>) => {
// Just mutate state
Object.assign(state, action.payload);
// or return new state value
// return action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(getUser.fulfilled, (state, action) => {
Object.assign(state, action.payload);
state.loading = false;
state.error = "";
});
builder.addCase(getUser.pending, (state) => {
state.loading = true;
state.error = "";
});
builder.addCase(getUser.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || "user not initialized";
});
},
});