Okay
Okay

Reputation: 83

Cannot read properties of undefined (reading 'includes') error after refreshing page

I am trying to make a web app using reactjs and I have been stuck on this error... I'm trying to add a like button on the product card and if user liked the product, the like button will be filled. When I am already on the page on localhost and I make changes in the code (updated immediately because I am using nodemon) the changes show up and work fine. However, once I refresh the page, or if I try to open the page again while the changes are present, I will get this error:

TypeError: Cannot read properties of undefined (reading 'includes')

Here is my code:

function Product(props) {
  const { product } = props;

  const { state } = useContext(Store);
  const { userInfo } = state;

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

  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});
      } catch(err) {
        dispatch({type: 'FETCHUSER_FAIL'});
        toast.error(getError(err));
      }
    }
    fetchUser();
  }, []);

  const [favourites, setFavourites] = useState([]);

  const likesHandler = async(e) => {
    e.preventDefault();
    favourites.push(product._id);
    setFavourites(favourites => [...favourites, product._id]);
      const { data } = await axios.put(
        '/api/users/likes',
        { favourites },
        {
          headers: {Authorization: `Bearer ${userInfo.token}`},
        }
      );
  };

  console.log(user);
  console.log(user.favourites);

  return (
    <Card styles="1px solid grey">
      <Link to={`/product/${product.slug}`}>
        <img src={product.image} className="card-img-top" alt={product.name} />
      </Link>
      <Card.Body>
        <Link to={`/product/${product.slug}`}>
          <Card.Title>{product.name}</Card.Title>
        </Link>
        <Rating rating={product.rating} numReviews={product.numReviews} />
        <Card.Text>${product.price}</Card.Text>
        {product.stock === 0 ? (
          <Button variant="light" disabled>
            Out of stock
          </Button>
        ) : (
          <Button onClick={() => addToCartHandler(product)}>Add to cart</Button>
        )}
      </Card.Body>

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

    </Card>
  );
}
export default Product;

My API code:

userRouter.get(
  '/profile',
  isAuth,
  expressAsyncHandler(async (req, res) => {
    const user = await User.findById(req.user._id);
    res.send(user);
  })
);

userRouter.put(
  '/likes',
  isAuth,
  expressAsyncHandler(async(req, res) => {
    const user = await User.findById(req.user._id);

    if (user) {
      user.favourites.push(req.body.favourites[0])
    }
    await user.save();
  })
);

After refreshing, console.log(user) will return empty and user.favourites will return undefined. I know it is probably an issue with how my user data is populated, but I cannot figure out what is the issue... I'm populating user right at the start of the code. If anyone has any idea what the issue could be, would be very grateful!

Upvotes: 0

Views: 13626

Answers (2)

fermin
fermin

Reputation: 21

Part of the issue you describe could be solved by using the optional chaining (?.) operator. In your case: ?.includes(product._id) instead of ?.includes(product._id). At least you could stop getting that error like that.

In my case, I was getting the same error in my javascript project because I was using .includes(); to check a variable that was sometimes 'undefined'. I solved it by using ?.includes(); .

I finished understanding it after reading this article, which states:

The optional chaining (?.) operator can also be used to avoid getting an error because if the reference is equal to undefined or null, it short-circuits returning undefined.

Upvotes: 2

SURViR
SURViR

Reputation: 82

I believe that error occurs because it you load your page form "fresh" start you have empty user array until it gets fetched from API - you have to check if it has properties you want to obtain.

In your case you can secure from this error by adding question mark before checking for object that may not exist when you looking for it.

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

If it is not defined code fill simply return false and return to "else" block of this instruction.

Upvotes: 3

Related Questions