在基于 React 18 + Vite 5 + React Router 6 开发「智能设备管理平台」时,我遇到了一个让用户体验直接 “降级” 的诡异 bug:从设备详情页返回设备列表页后,列表页的筛选条件、分页状态会被直接清空,仿佛路由得了 “健忘症”。更气人的是,控制台连一条报错信息都没有,bug 像 “幽灵” 一样隐形。
项目里,设备列表页需要支持「按设备类型筛选」「按在线状态过滤」「分页加载」等功能,我用 Zustand 做全局状态管理,把筛选条件存在 useDeviceStore
里。原本预期:从详情页返回列表页时,筛选状态会被保留,方便用户继续操作。
但实际表现是:点击返回按钮(或浏览器回退)后,列表页直接回到 “初始状态”—— 筛选条件全没了,分页也回到第 1 页。更诡异的是,控制台没有任何报错,Zustand 的 devtools 里能看到状态 “突然被重置”,却找不到是谁在修改状态。
作为 “资深” 前端,我先从经典方向排查,结果全是 “无效努力”:
useLocation
+ state
传递状态,发现返回时 state
确实存在,但 Zustand 里的状态就是不更新 —— 排除路由传参问题。useEffect
里有订阅筛选状态的逻辑,逐一检查依赖数组,没发现 “意外触发重渲染” 的漏洞。React.memo
,甚至给 Zustand 的 selector 用 createSelector
做缓存 —— 状态还是 “说没就没”。折腾 2 小时后,bug 像透明罩子,把我困在里面,看不到突破口。
被逼到墙角时,我打开 Cursor(AI 编程助手),输入问题描述:
「React 18 + Vite 5 + React Router 6 + Zustand 项目中,从详情页返回列表页后,Zustand 状态被重置,控制台无报错。试了路由 state、useEffect 依赖、组件 memo 都没解决,可能原因是?」
Cursor 瞬间给出 “精准到可怕” 的猜测:
zustand/middleware/persist
可能因存储 key 或序列化逻辑,导致路由切换时状态被清空。其中,“Vite 缓存 + Zustand 持久化” 让我眼前一亮 —— 立刻检查 persist
配置:
ts
// store/device.ts 原配置
import { create } from 'zustand';
import { persist } from 'zustand/middleware/persist';
export const useDeviceStore = create(
persist(
(set, get) => ({
filters: {},
page: 1,
}),
{ name: 'device-store' } // 未指定 storage,默认用 localStorage
)
);
再看 Vite 配置(vite.config.ts
),发现没对 Zustand 模块做 HMR 排除。
结合 Cursor 提示,我推测:Vite 开发环境下,模块热更新导致 Zustand 持久化存储 “混乱”—— 路由跳转时,Vite 刷新模块,Zustand 误把 “初始状态” 当成最新状态,覆盖了内存中的状态。
解决方案分两步:
开发环境(development
)下,关闭 Zustand 持久化(避免和 Vite HMR 冲突);生产环境(production
)下再开启。
修改 store/device.ts
:
ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware/persist';
const isDev = import.meta.env.DEV; // Vite 注入的环境变量
export const useDeviceStore = create(
isDev
? (set, get) => ({ // 开发环境:不持久化,靠路由传参保状态
filters: {},
page: 1,
setFilters: (filters) => set({ filters }),
setPage: (page) => set({ page }),
})
: persist( // 生产环境:开启 localStorage 持久化
(set, get) => ({
filters: {},
page: 1,
setFilters: (filters) => set({ filters }),
setPage: (page) => set({ page }),
}),
{ name: 'device-store' }
)
);
用 React Router 的 useNavigate
和 useLocation
,在路由跳转时手动保留状态:
tsx
// DeviceList.tsx
import { useNavigate, useLocation } from 'react-router-dom';
import { useDeviceStore } from '@/store/device';
const DeviceList = () => {
const navigate = useNavigate();
const location = useLocation();
const { filters, page, setFilters, setPage } = useDeviceStore();
// 跳详情页时,把状态存在 location.state
const goToDetail = (deviceId) => {
navigate(`/device/${deviceId}`, {
state: { filters, page },
});
};
// 组件挂载时,从 location.state 恢复状态(开发环境专属)
React.useEffect(() => {
if (import.meta.env.DEV && location.state?.filters) {
setFilters(location.state.filters);
setPage(location.state.page);
}
}, [location, setFilters, setPage]);
// 其他渲染逻辑...
};
修改后重启项目,奇迹发生了:
localStorage
持久化正常,状态也不再丢失。
控制台的 “寂静” 终于成了 “无错可报”。这次 debug 让我收获两点关键认知:
现在再看那个 “失忆” 的路由,它更像个提醒:前端开发的乐趣,就藏在 “工具协作” 与 “逻辑拆解” 的博弈里。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。