React中LocalStorage与useState页面刷新竞态问题修复咨询
修复购物车LocalStorage存储的竞态问题
问题根源
你的代码中,cartItems初始化为空数组[],会触发依赖它的useEffect将LocalStorage清空;而加载LocalStorage数据的useEffect是异步执行的,导致旧数据还没加载完成就被初始空数组覆盖,出现竞态条件。
修复方案
方案一:函数式初始化State(推荐)
利用React的函数式初始State,直接从LocalStorage读取初始值,同时避免服务器端渲染时window未定义的报错:
"use client"; import React, { useState, useEffect } from "react"; export default function Home() { // 函数式初始值,仅在组件首次渲染时执行 const [cartItems, setCartItems] = useState(() => { // 先判断window是否存在,避免SSR报错 if (typeof window !== "undefined") { const savedCart = localStorage.getItem("cartItems"); return savedCart ? JSON.parse(savedCart) : []; } return []; }); // 仅在cartItems变化时存储到LocalStorage useEffect(() => { console.log("storing cart items", JSON.stringify(cartItems)); localStorage.setItem("cartItems", JSON.stringify(cartItems)); }, [cartItems]); return <div>{JSON.stringify(cartItems)}</div>; }
这种方式直接跳过了初始空数组触发存储的步骤,从根源上解决竞态问题,同时兼容服务器端渲染场景。
方案二:用useRef标记初始化状态
如果不想修改初始State的方式,可以通过useRef标记组件是否完成初始化,避免初始空数组触发存储:
"use client"; import React, { useState, useEffect, useRef } from "react"; export default function Home() { const [cartItems, setCartItems] = useState([]); // 标记是否完成LocalStorage数据加载 const isInitialized = useRef(false); useEffect(() => { const cartItemsData = JSON.parse(localStorage.getItem("cartItems")); if (cartItemsData) { console.log("loaded cart items data", cartItemsData); setCartItems(cartItemsData); } // 加载完成后标记初始化完成 isInitialized.current = true; }, []); useEffect(() => { // 仅初始化完成后才执行存储操作 if (isInitialized.current) { console.log("storing cart items", JSON.stringify(cartItems)); localStorage.setItem("cartItems", JSON.stringify(cartItems)); } }, [cartItems]); return <div>{JSON.stringify(cartItems)}</div>; }
关于空字符串初始值能工作的原因
当初始值设为空字符串时,首次触发存储的是"",后续加载LocalStorage时JSON.parse("")会返回null,不会覆盖购物车数据;但这种方式属于临时规避,不规范且可能引发其他问题,建议使用上面的标准方案修复。
内容的提问来源于stack exchange,提问作者DD.
相关产品推荐
相关产品推荐

