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

Next.js使用js-cookie存储数组刷新后数据异常解决方案

问题根因

出现数据错乱、重复的核心原因有三个:

  • 序列化与反序列化逻辑不匹配:写入Cookie时使用JSON.stringify()将数组转为JSON格式字符串,读取时却直接调用split(',')做字符串拆分。JSON序列化后的数组会携带方括号、元素双引号,例如["London","Kyiv","Los Angeles"],直接按逗号分割会把括号、引号混入数组元素,下次写入时又会对错误内容再次做JSON序列化,多次刷新后内容会不断叠加、格式彻底错乱。
  • Next.js水合阶段逻辑冲突:js-cookie是纯浏览器端API,服务端渲染阶段无法访问浏览器Cookie,组件首次在服务端渲染时会直接使用默认城市列表生成HTML,客户端水合完成后useEffect触发,会把当前渲染用的默认值写入Cookie,覆盖用户之前存储的自定义数据,和序列化错误叠加后,刷新次数越多异常越明显。
  • Cookie配置不规范:写入Cookie时path参数传空字符串不符合规范,会导致Cookie作用域异常,不同路由路径下可能读取到不同的Cookie副本,进一步造成数据混乱。
最优修复方案

不需要额外做重复写入判断,只要修正三个核心逻辑即可:

  1. 统一序列化/反序列化逻辑:读取Cookie时使用JSON.parse()解析JSON字符串,增加解析失败的兜底逻辑
  2. 规避SSR水合问题:不在useState初始化阶段直接读取Cookie,改为客户端挂载后再读取Cookie初始化状态,避免服务端渲染用默认值覆盖用户存储的数据
  3. 修正Cookie配置:将path设为/保证全站共享同一份Cookie,同时增加空值校验避免存入无效内容

修复后的完整代码如下:

import { LineChartProps } from "./LineChart.props";
import { Input } from "../Input/Input";
import { Button } from "../Button/Button";
import cookie from "js-cookie";
import React, { useState, useEffect } from "react";

export const LineChart = ({ }: LineChartProps): JSX.Element => {
    const [inputValue, setInputValue] = useState("");
    // 初始化先设为空数组,避免SSR阶段读取Cookie报错
    const [citiesList, setCitiesList] = useState<string[]>([]);
    const [isLoaded, setIsLoaded] = useState(false);

    // 客户端挂载后再读取Cookie初始化状态,解决水合冲突
    useEffect(() => {
        const storedCities = cookie.get("citiesList");
        if (storedCities) {
            try {
                // 统一用JSON.parse解析,和写入时的JSON.stringify对应
                const parsedCities = JSON.parse(storedCities);
                // 校验解析结果是数组再赋值
                if (Array.isArray(parsedCities)) {
                    setCitiesList(parsedCities);
                } else {
                    setCitiesList(["London", "Kyiv", "Los Angeles"]);
                }
            } catch (e) {
                // 解析失败时用默认值,同时清除错误Cookie
                setCitiesList(["London", "Kyiv", "Los Angeles"]);
                cookie.remove("citiesList", { path: "/" });
            }
        } else {
            setCitiesList(["London", "Kyiv", "Los Angeles"]);
        }
        setIsLoaded(true);
    }, []);

    const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setInputValue(event.target.value); 
    };

    const onClick = () => {
        // 过滤空输入,避免存入无效值
        if (!inputValue.trim()) return;
        setCitiesList((currentArray) => [...currentArray, inputValue.trim()]);
        setInputValue("");
    };

    // 仅在客户端初始化完成后再监听状态变化写入Cookie
    useEffect(() => {
        if (!isLoaded) return;
        // 修正path为根路径,统一作用域
        cookie.set("citiesList", JSON.stringify(citiesList), { path: "/" });
    }, [citiesList, isLoaded]);

    return (
        <div>
            <Button appearance="primary" onClick={onClick}>click me</Button>
            <Input type="text" name="search" value={inputValue} onChange={onChange} />
            <div>
                <p>{inputValue}</p>
                {/* 初始化完成后再渲染城市列表 */}
                {isLoaded && citiesList.map(city => <span key={city} style={{marginRight: '8px'}}>{city}</span>)}
            </div>
        </div>
    )
};
额外注意事项
  • Cookie单条存储上限通常为4KB,如果后续城市列表数据量较大,建议替换为localStorage存储,API使用逻辑和上述方案一致,且不需要额外配置path作用域。
  • 如果需要在服务端渲染阶段就获取用户存储的城市列表,不能直接在组件中调用js-cookie,需要在Next.js的getServerSideProps或App Router的服务端组件中从请求头Cookie字段解析获取后,通过props传递给客户端组件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 16:18:21