“根减速器”类型不匹配

问题描述 投票:0回答:1

在我的 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 接口的方式的问题。

typescript redux state
1个回答
0
投票

我认为问题在于你的

updateUser
,至少有几个问题。

  1. 实际上不需要参数化
    createAsyncThunk
    ,因为它应该已经根据存储和有效负载创建者参数推断出正确的类型。
  2. updateUser
    不是 React 组件或自定义 React hook,因此它不能使用
    useSelector
    钩子。使用
    thunkApi.getState
    访问当前状态值。
  3. 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";
    });
  },
});
© www.soinside.com 2019 - 2024. All rights reserved.