Search code examples
reactjsaxiosgetstorage

How and where to store certain data in React


so I want to ask such a question and get advice. I would be very happy if experienced developers give their opinion. Thank you in advance <<3 So I have displayed the products from the user's api, if the user has made a favorite it should be a filled heart otherwise it should be an empty heart. But I don't know how to keep it so that it doesn't get lost in the refresh. In general, how do you store this type of data on the client side? There is also such a problem that, for example, in this code, it is completely fixed only after 1-2 refreshes, the first time it is opened, the favorite product comes with an empty heart.

import React, { useEffect, useState } from "react";
import heartOn from "../../assets/icons/heart-on.svg";
import heartFull from "../../assets/icons/Subtract.svg";
import { handleToggleWishlist } from "../../helpers";
import useAxios from "../../utils/useAxios";
import { toast } from "react-toastify";

const WishBtn = ({ ProductItemVideoCard }) => {
  const axiosInstance = useAxios();
  const [in_wishlist, set_in_wishlist] = useState(false);

  useEffect(() => {
    if (ProductItemVideoCard) {
      set_in_wishlist(ProductItemVideoCard?.in_wishlist);
    }
  }, [ProductItemVideoCard]);

  useEffect(() => {
    const updateWishlistState = (event) => {
      const { productId, status } = event.detail;
      if (productId === ProductItemVideoCard?.id) {
        set_in_wishlist(status);
      }
    };

    window.addEventListener("wishlistUpdate", updateWishlistState);

    return () => {
      window.removeEventListener("wishlistUpdate", updateWishlistState);
    };
  }, [ProductItemVideoCard?.id]);

  useEffect(() => {
    if (ProductItemVideoCard) {
      const wishlistState = JSON.parse(localStorage.getItem("wishlist")) || {};
      set_in_wishlist(wishlistState[ProductItemVideoCard.id] || false);
    }
  }, [ProductItemVideoCard?.id]);

  const handleWishlistToggle = async (productId) => {
    const newStatus = await handleToggleWishlist(productId, axiosInstance);
    if (newStatus !== null) {
      set_in_wishlist(newStatus);
      const wishlistState = JSON.parse(localStorage.getItem("wishlist")) || {};
      if (newStatus) {
        wishlistState[productId] = newStatus;
      } else {
        delete wishlistState[productId];
      }
      localStorage.setItem("wishlist", JSON.stringify(wishlistState));

      const event = new CustomEvent("wishlistUpdate", {
        detail: { productId, status: newStatus },
      });
      window.dispatchEvent(event);
    } else {
      console.log("Failed to update wishlist status");
    }
  };

  if (!ProductItemVideoCard) return null;

  return (
    <img
      src={in_wishlist ? heartFull : heartOn}
      alt=""
      onClick={() => handleWishlistToggle(ProductItemVideoCard.id)}
    />
  );
};

export default WishBtn;

Let me note that the value is_wishlist comes from the app, if I liked it, it is true, otherwise it is false by default


Solution

  • Storing this in the localstorage is one way to do this. From your code, it seems like you don't initialise the in_wishlist state with what's in the localstorage. You can do this with a useEffect with no dependency which will then run on the first render (and trigger re-rendering if it is in the wish list), something like this:

      useEffect(() => {
        if (ProductItemVideoCard) {
          const localWishlist = JSON.parse(localStorage.getItem("wishlist")) || {};
          set_in_wishlist(localWishlist[ProductItemVideoCard.id] || false);
        }
      }, []);
    

    But I would say that for this it might be better to use some kind of server state associated with the user account so that it would appear on different platforms.

    Also camelCase is more conventional than snake_case in JS.