You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React项目页面刷新后出现TypeError: Cannot read properties of undefined (reading 'includes')错误求助

Fixing "Cannot read properties of undefined (reading 'includes')" in Your React Product Card

Hey there! Let's break down what's causing that error when you refresh the page, and fix it up step by step.

The Root Cause

When your component first mounts (like after a page refresh), your user state from useReducer starts as an empty array []—but user data from your API is an object, not an array. On the initial render, before your useEffect finishes fetching the user profile, you try to access user.favourites on that empty array. Since arrays don't have a favourites property, this returns undefined, and calling .includes() on undefined throws the TypeError you're seeing.

Step 1: Fix the Initial User State

First, update your useReducer initial state to use an empty object instead of an empty array—this matches the shape of the user data your API returns:

const [{ loading, user }, dispatch] = useReducer(reducer, { loading: false, user: {} });

Step 2: Add Guard Clauses & Loading State

Next, add checks to make sure you don't try to access user.favourites until the user data is loaded. You can use optional chaining (?.) for a quick check, or handle the loading state explicitly:

Option A: Optional Chaining (Quick Fix)

Modify the heart icon condition to use ?. to safely access favourites:

{user.favourites?.includes(product._id) ? (
  <i className="fas fa-heart fa-lg card-icon-btm"></i>
) : (
  <span onClick={likesHandler}>
    <i className="far fa-heart fa-lg card-icon-btm"></i>
  </span>
)}

Option B: Explicit Loading State (Better UX)

Show a loading indicator while the user data is being fetched, so users don't see a broken UI:

if (loading) {
  return <div>Loading...</div>;
}

// If user data isn't loaded yet, return a fallback or nothing
if (!user || Object.keys(user).length === 0) {
  return null;
}

// Rest of your return statement goes here

Step 3: Fix the likesHandler Logic

Your current likesHandler has a bug where you directly mutate the favourites state array with favourites.push(product._id)—this doesn't work with React's state management. Also, you're calling setFavourites twice with conflicting logic. Let's clean that up:

const likesHandler = async(e) => {
  e.preventDefault();
  // Create a new array with the product ID added (avoid mutating state directly)
  const updatedFavourites = [...favourites, product._id];
  setFavourites(updatedFavourites);
  
  // Send the updated favourites to your API
  try {
    await axios.put(
      '/api/users/likes',
      { productId: product._id }, // Send just the product ID instead of the whole array
      { headers: { Authorization: `Bearer ${userInfo.token}` } }
    );
    // After successful API call, refetch the user profile to sync state
    fetchUser();
  } catch(err) {
    toast.error(getError(err));
    // Revert the state if the API call fails
    setFavourites(favourites);
  }
};

Step 4: Optimize Your API Endpoint

Your current PUT /api/users/likes endpoint pushes the first element of req.body.favourites every time, which can lead to duplicate entries. Let's update it to check if the product is already in favourites before adding it:

userRouter.put(
  '/likes',
  isAuth,
  expressAsyncHandler(async(req, res) => {
    const user = await User.findById(req.user._id);
    if (user) {
      // Only add the product if it's not already in favourites
      if (!user.favourites.includes(req.body.productId)) {
        user.favourites.push(req.body.productId);
        await user.save();
        res.send({ message: 'Favourite added' });
      } else {
        res.send({ message: 'Product already in favourites' });
      }
    } else {
      res.status(404).send({ message: 'User not found' });
    }
  })
);

Step 5: Sync Local State with API Data

After fetching the user profile in useEffect, update your local favourites state to match the user's actual favourites from the API—this ensures local state is in sync with the database after a refresh:

useEffect(() => {
  const fetchUser = async () => {
    dispatch({type: 'FETCHUSER_REQUEST'});
    try {
      const user = await axios.get(
        '/api/users/profile',
        { headers: {Authorization: `Bearer ${userInfo.token}`} }
      );
      dispatch({type: 'FETCHUSER_SUCCESS', payload: user.data});
      // Update local favourites state with the user's saved favourites
      setFavourites(user.data.favourites || []);
    } catch(err) {
      dispatch({type: 'FETCHUSER_FAIL'});
      toast.error(getError(err));
    }
  }
  fetchUser();
}, []);

Final Notes

  • Don't forget to use className instead of class in JSX (you had class="fas fa-heart..."—this should be className).
  • Adding error handling in likesHandler ensures that if the API call fails, your local state doesn't get out of sync with the database.

That should fix the refresh error and make your like button work reliably!

内容的提问来源于stack exchange,提问作者Okay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 21:02:30