错误:操作必须是普通对象。使用自定义中间件进行异步操作。 Redux/工具包

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

当尝试将产品添加到用户收藏夹时,我收到此错误。尽管 redux 的状态正在正确更新。 我已经使用 Redux Thunk 进行身份验证,但我试图避免添加许多额外的减速器,因为只为一个函数添加大量代码;并开始使用productService.js文件。

userSlice.js

import {
  createAction,
  createSlice,
} from "@reduxjs/toolkit";
import {
  loginUser,
  registerUser,
} from "../actions/authActions";
import { getWithExpiry } from "../../utils/setStorage";

export const addFavoritesError = createAction(
  "addFavoritesError"
);

const savedUserInfo = getWithExpiry("userInfo")
  ? getWithExpiry("userInfo")
  : null;
const savedToken = getWithExpiry("userToken")
  ? getWithExpiry("userToken")
  : null;

const initialValue = {
  isLoading: false,
  userInfo: savedUserInfo,
  userToken: savedToken,
  error: null,
  success: false,
};

const authSlice = createSlice({
  name: "auth",
  initialState: initialValue,
  reducers: {
    login: (state) => {
      loginUser().then((res) => {
        // history.pushState('/');
      });
    },

    logOut: (state) => {
      return {
        ...state.initialState,
        isLoading: false,
        userInfo: null,
        userToken: null,
        error: null,
        success: false,
      };
    },
    signUp: (state) => {
      registerUser().then((res) => {
        // history.pushState('/');
      });
    },

    addFavorites: (state, action) => {
      return {
        ...state,
        userInfo: {
          ...state.userInfo,
          favorites: [...action.payload],
        },
      };
    },
  },
  extraReducers: {
    // 1 login user
    [loginUser.pending]: (state) => {
      state.isLoading = true;
      state.error = null;
    },

    [loginUser.fulfilled]: (state, { payload }) => {
      state.isLoading = false;
      state.success = true;
      state.userInfo = payload.data.user;
      state.userToken = payload.token;
    },

    [loginUser.rejected]: (state, { payload }) => {
      state.isLoading = false;
      state.error = payload;
    },

    // 2 register user

    [registerUser.pending]: (state) => {
      state.isLoading = true;
      state.error = null;
    },

    [registerUser.fulfilled]: (state, { payload }) => {
      state.isLoading = false;
      state.success = true;
      state.userInfo = payload;
      state.userToken = payload.token;
    },

    [registerUser.rejected]: (state, { payload }) => {
      state.isLoading = false;
      state.error = payload;
    },
  },
});

export const { logOut, addFavorites } = authSlice.actions;

export default authSlice.reducer

userService.js

import apiActions from "../../utils/api";
import { addFavorites, addFavoritesError } from "../slices/userSlice";

export const AddProdToFavorites = async (dispatch, user_id, prod_id) => {
  try {
    // api call
    const data = await apiActions.addProdToUserFav(user_id, prod_id);
    dispatch(addFavorites(data));
  } catch {
    dispatch(addFavoritesError());
  }
};`

ProductCard.js

import React, { useCallback, useState } from "react";
import { useDispatch} from "react-redux";
import { AddProdToFavorites} from "../../../store/services/userService";


function DestinationsDisplayCard(props) {
  const { userToken, name, price} = props;
  const [favored, setFavored] = useState(false);

  const dispatch = useDispatch();
  const addItemtoFav = useCallback(() => {
    setFavored((currrent) => !currrent);
    dispatch(AddProdToFavorites(dispatch, userToken, prodId));
  }, [favored]);

const removeItemFromFav = useCallback(() => {}, [favored]);

return (
  <div className="productCard">
        <p>{name}</p>
        <p>${price}</p>
        {userToken ? (
          <button
            onClick={favored ? removeItemFromFav : addItemtoFav}
          >
          </button>
        ) : null}
      </div>

)
}

api.js

import axios from "axios";
const baseURL = "<<the url for api>>";

const apiActions = {
addProdToUserFav: async (user_id, prod_id) => {
    const res = await axios.post(
      `${baseURL}/v1/user/addFavorite`,
      {
        userId: user_id,
        prodId: prod_id,
      }
    );
    return res.data.data.user.favorites;
  },
// this endpoint returns a list of object [{product2}{product2}] (with name, price, discount, etc)
}

我预计 state.userInfo.favorites 会更新,它确实如此,但我收到了此错误。

Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
VM41:1 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
    at Object.performAction (<anonymous>:1:41504)
    at k (<anonymous>:3:1392)
    at s (<anonymous>:3:5102)
    at serializableStateInvariantMiddleware.ts:210:1
    at index.js:20:1
    at Object.dispatch (immutableStateInvariantMiddleware.ts:264:1)
    at dispatch (<anonymous>:6:7391)
    at DestinationsDisplay.card.jsx:23:1
    at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
reactjs react-hooks redux-toolkit redux-thunk
1个回答
0
投票

“未捕获的错误:操作必须是普通对象”是您看到的错误。 “使用自定义中间件进行异步操作”意味着您尝试在不使用 Redux Thunk 或 Redux Saga 等中间件的情况下分派异步操作。 Redux 中的异步操作需要中间件来处理。如果您还没有安装,请下载并安装 Redux Thunk:

npm install redux-thunk

Redux Thunk 现在应该包含在您的 Redux 存储配置中:

// store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers'; // Replace with your actual reducer file

const store = createStore(rootReducer, applyMiddleware(thunk));

export default store;

修改您的

AddProdToFavorites
方法,使其返回一个异步执行操作的函数。调用
AddProdToFavorites
方法时,您必须另外提供
user_id
prod_id
作为参数:

// userService.js
import apiActions from "../../utils/api";
import { addFavorites, addFavoritesError } from "../slices/userSlice";

export const AddProdToFavorites = (user_id, prod_id) => async (dispatch) => {
  try {
    // api call
    const data = await apiActions.addProdToUserFav(user_id, prod_id);
    dispatch(addFavorites(data));
  } catch {
    dispatch(addFavoritesError());
  }
};

修改您的组件以适当地调用

AddProdToFavorites
函数:

// ProductCard.js
import React, { useCallback, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; // Import useSelector
import { AddProdToFavorites } from "../../../store/services/userService";
import { selectUserInfo } from "../../../store/slices/userSlice"; // Import selector

function DestinationsDisplayCard(props) {
  const { userToken, name, price, prodId } = props;
  const [favored, setFavored] = useState(false);

  const dispatch = useDispatch();
  const userInfo = useSelector(selectUserInfo); // Use useSelector to access user info

  const addItemtoFav = useCallback(() => {
    setFavored((current) => !current);
    dispatch(AddProdToFavorites(userInfo.id, prodId)); // Pass user_id and prod_id
  }, [favored, userInfo, prodId]);

  const removeItemFromFav = useCallback(() => {}, [favored]);

  return (
    <div className="productCard">
      <p>{name}</p>
      <p>${price}</p>
      {userToken ? (
        <button onClick={favored ? removeItemFromFav : addItemtoFav}></button>
      ) : null}
    </div>
  );
}

export default DestinationsDisplayCard;

小心地将

selectUserInfo
替换为 Redux 存储中提取用户信息的实际选择器。

© www.soinside.com 2019 - 2024. All rights reserved.