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

React客户端收藏状态持久化及组件状态异常问题咨询

商品收藏爱心状态问题排查与修复

问题描述

  • 刷新页面后收藏状态丢失,客户端未持久化收藏数据
  • 首次打开页面时,已收藏商品显示空心爱心,多次刷新后才恢复正常

提供的组件代码

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;

补充说明:in_wishlist字段来自后端,商品收藏时为true,默认值为false


问题分析

  1. 状态更新冲突:三个useEffect同时修改in_wishlist状态,且执行顺序不可控,首次渲染时后端返回的真实收藏状态会被localStorage的空值覆盖(初始为空,设为false)
  2. 客户端与后端状态不同步:首次加载未优先同步后端状态,导致显示错误;刷新后依赖localStorage但未保证其与后端一致

修复方案

核心优化点

  • 合并重复的useEffect,明确状态更新顺序
  • 确保后端状态、组件状态、localStorage三者双向同步
  • 用函数式初始化状态,避免props依赖问题

修复后的完整代码

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();
  // 函数式初始化:优先从localStorage读取,无数据则设为false
  const [in_wishlist, set_in_wishlist] = useState(() => {
    if (!ProductItemVideoCard) return false;
    const wishlistState = JSON.parse(localStorage.getItem("wishlist")) || {};
    return wishlistState[ProductItemVideoCard.id] || false;
  });

  useEffect(() => {
    if (!ProductItemVideoCard) return;

    // 首次加载:用后端真实状态同步组件和localStorage
    const backendStatus = ProductItemVideoCard.in_wishlist;
    set_in_wishlist(backendStatus);
    
    const wishlistState = JSON.parse(localStorage.getItem("wishlist")) || {};
    backendStatus ? wishlistState[ProductItemVideoCard.id] = backendStatus : delete wishlistState[ProductItemVideoCard.id];
    localStorage.setItem("wishlist", JSON.stringify(wishlistState));

    // 监听全局收藏更新事件,同步状态和localStorage
    const updateWishlistState = (event) => {
      const { productId, status } = event.detail;
      if (productId === ProductItemVideoCard.id) {
        set_in_wishlist(status);
        const wishlistState = JSON.parse(localStorage.getItem("wishlist")) || {};
        status ? wishlistState[productId] = status : delete wishlistState[productId];
        localStorage.setItem("wishlist", JSON.stringify(wishlistState));
      }
    };

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

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

      // 触发全局事件同步其他组件
      window.dispatchEvent(new CustomEvent("wishlistUpdate", {
        detail: { productId, status: newStatus },
      }));
    } else {
      console.log("Failed to update wishlist status");
    }
  };

  if (!ProductItemVideoCard) return null;

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

export default WishBtn;

优化说明

  • 状态初始化时优先读取localStorage,保证刷新后状态不丢失
  • 首次加载强制用后端状态同步客户端存储,解决首次显示错误问题
  • 合并useEffect逻辑,避免状态冲突
  • 添加语义化alt属性,提升页面可访问性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 09:35:04