JWT 4:Zustand 到底解决了什么?为什么登录状态要放进 Store?

前面我们已经解决了:

  • Token 怎么生成
  • Token 怎么发送
  • Token 怎么验证

但 React 还有一个自己的问题:

页面怎么知道用户现在处于登录状态?

例如导航栏要决定显示:

text 复制代码
Login

还是:

text 复制代码
Logout

Pay 页面也需要知道:

当前用户有没有登录。

这时候,单靠 localStorage 并不是最舒服的解决方式。


一、浏览器存储和 React 状态不是一回事

登录成功以后:

js 复制代码
localStorage.setItem('token', token);

浏览器当然记住了 Token。

但是 React 组件要不要重新渲染?

这不是 localStorage 自动帮你解决的。

React 更关心的是:

text 复制代码
当前状态是什么?

所以我们需要一个专门管理:

text 复制代码
token
user

的地方。

这就是 Store。


二、为什么不直接用 Props?

假设 App 有:

text 复制代码
Nav
Login
Home
Pay

登录后:

text 复制代码
Login

拿到了:

text 复制代码
token
user

但是:

text 复制代码
Nav
Pay
RequireAuth

也都需要这个信息。

如果使用 Props:

text 复制代码
App
 ↓
Nav
 ↓
...

可能需要一层一层往下传。

登录状态明显属于:

整个应用共享的数据。

所以很适合放到全局 Store。


三、创建 Zustand Store

先创建:

text 复制代码
src/store/

然后:

text 复制代码
src/store/user.js

因为这个 Store 保存的是:

当前用户的身份和登录状态。

代码:

js 复制代码
import { create } from 'zustand';

然后:

js 复制代码
export const useAuthStore = create(set => ({
  token: '',
  user: null,

  setAuth: ({ token, user }) => {
    // ...
  },

  logout: () => {
    // ...
  }
}));

四、为什么 create(set => ({})) 看起来很奇怪?

这也是 Zustand 最容易让初学者懵住的地方。

js 复制代码
create(set => ({
  token: '',
  user: null
}))

可以先把它看成:

create() 需要你告诉它"这个 Store 里面有哪些状态和修改状态的方法"。

而:

js 复制代码
set

是 Zustand 提供的:

修改 Store 状态的方法。

所以:

js 复制代码
set({
  token,
  user
});

就是:

把 Store 里的 token 和 user 改掉。


五、Store 里面保存什么?

当前代码:

js 复制代码
export const useAuthStore = create(set => ({
  token: localStorage.getItem('token') || '',
  user: initialUser,

这里有两个最核心的状态:

text 复制代码
token
user

token 表示:

当前有没有登录凭证。

user 表示:

当前登录的是谁。


六、为什么 Store 初始化时还要读取 localStorage?

因为 Zustand 的状态本质上还是内存里的。

如果你刷新页面:

text 复制代码
刷新
 ↓
React 重新启动
 ↓
原来的内存状态消失

但:

text 复制代码
localStorage

不会因为刷新就消失。

因此我们在创建 Store 时:

js 复制代码
token: localStorage.getItem('token') || ''

就可以把之前保存的 Token 恢复回来。

用户信息也类似。


七、为什么 user 要 JSON.parse?

localStorage 只能保存字符串。

保存对象时:

js 复制代码
localStorage.setItem(
  'user',
  JSON.stringify(user)
);

读取的时候:

js 复制代码
const savedUser = localStorage.getItem('user');

得到的是字符串。

所以需要:

js 复制代码
JSON.parse(savedUser)

把字符串重新变成 JavaScript 对象。


八、登录成功后修改 Store

Login 页面:

js 复制代码
const setAuth = useAuthStore(
  state => state.setAuth
);

登录:

js 复制代码
const res = await login(formData);

成功以后:

js 复制代码
setAuth({
  token: res.token,
  user: res.user
});

Store 里的:

text 复制代码
token
user

就更新了。

同时 setAuth() 里还会:

js 复制代码
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));

所以它同时完成两件事:

text 复制代码
更新 React 当前状态
+
保存登录状态

九、为什么还需要 logout?

登录状态不是只能增加,当然也需要删除。

所以:

js 复制代码
logout: () => {
  localStorage.removeItem('token');
  localStorage.removeItem('user');

  set({
    token: '',
    user: null
  });
}

这里同样做了两件事情:

text 复制代码
localStorage 清掉
+
Zustand 清掉

这样整个应用就重新变成:

text 复制代码
未登录状态

十、Nav 为什么可以直接知道有没有登录?

因为 Nav 不需要自己找 localStorage。

它直接:

js 复制代码
const token = useAuthStore(
  state => state.token
);

有 Token:

jsx 复制代码
{token && <button>Logout</button>}

没有 Token:

jsx 复制代码
{!token && <Link to="/login">Login</Link>}

所以你会发现:

Zustand 让不同组件共享"当前登录状态"。

这就是它在这个项目里的核心价值。


十一、现在这个项目有两个"地方"保存登录状态

可以把它们分成两个角色:

text 复制代码
localStorage
负责:
刷新页面后还能找到 Token

Zustand
负责:
React 运行过程中共享登录状态

所以它们并不是重复的。

而是:

一个负责持久化,一个负责运行时状态。

这句话非常值得记住。

相关推荐
卷无止境3 小时前
智能体开发环境ADE浅析,编程工具的下一次范式跃迁
后端·python
码事漫谈5 小时前
DeepSeek 这波操作很凶
后端
CharlesYu016 小时前
前端性能优化的第一性原理,是不断缩短“用户发起意图 → 获得可用结果”之间的时间
前端
Bs_MoneyMagnet6 小时前
基于springboot+vue的医疗健康便民服务平台的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring
平头哥技术团队7 小时前
Day 21 _ 页内锚点_给每段起个 id,目录写 href=_#id_,点一下页面就滚到那一段
前端·html·html5
子兮曰8 小时前
Bun v1.4.1 深度解析:从 Zig 到 Rust,一场 11 天、64 个 AI 代理的语言迁徙
前端·后端·bun
人民广场吃泡面8 小时前
什么是AI Agent?它又能给前端带来哪些效率提升?
前端·人工智能
中科三方8 小时前
两家域名注册商资质被ICANN终止:企业域名资产安全再受关注
前端·网络·安全·域名
bug总结9 小时前
uniapp vue3全局方法注册使用
前端·javascript·uni-app
华无丽言9 小时前
如何在宜搭中实现获取子表中的字段值赋值到父表中?
前端·javascript·低代码