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 运行过程中共享登录状态

所以它们并不是重复的。

而是:

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

这句话非常值得记住。

相关推荐
前端snow5 小时前
ai agent --- mem0 外挂记忆系统
前端
不才不才不不才5 小时前
Spring 源码系列(29): 20 道高频面试题源码级解析合集
java·后端·spring
索隆zoro5 小时前
Army 的可插拔架构:army-jdbc 与方言模块
java·后端
小蒜学长6 小时前
校园社团招新网站的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·校园社团招新网站
特立独行的猫a6 小时前
用仓颉写一个 Tauri:IPC 的每次往返实现原理(web层到仓颉层的触发过程)
前端·ui·harmonyos·tauri·鸿蒙·仓颉
Dovis(誓平步青云)6 小时前
多个链接不等于多份证据,新闻核验看板怎样合并来源
java·服务器·前端·javascript·人工智能·pdf·电脑
骑着蜗牛撵大象3276 小时前
Qt 事件机制详解:从 QEvent 派生类到事件过滤器的全景指南
前端·python
qq_2518364577 小时前
理发管理系统 —— 会员模块
开发语言·前端·python·flask
Frag0ut7 小时前
2027 年浏览器展望:Chrome 与 Edge 的 AI 智能体进化方向
前端·人工智能·chrome·edge·浏览器·新功能
蜗牛互联网7 小时前
从Barclays扩大Claude部署看受监管软件工程的责任重分配
java·大数据·人工智能·后端·软件工程