前言:Redux 真的太难了...
作为一个刚入坑 React 不久的小白,我最近真的被状态管理搞得头皮发麻!
跟着教程学 Redux,一会儿 Action,一会儿 Reducer,一会儿又是 Selector... 我只是想存个数字,却要写一堆模板代码,文件切来切去,人都绕晕了。直到昨天,我在社区看到大佬安利 Zustand,号称只有 1KB,而且不用包组件,不用写 Provider。
我不信邪试了一下... 哇!这也太香了吧!
它写起来就像原生 JS 一样简单粗暴,配合 TypeScript 的智能提示,简直是为我们这种"手残党"量身定做的!今天就迫不及待把我的学习笔记(源码)分享给大家,希望能帮到同样迷茫的小伙伴!
第一关:从最简单的计数器开始
以前用 Redux 写个计数器要建好几个文件,用 Zustand 居然只要一个函数就搞定?
TypeScript
typescript
import { create } from 'zustand';
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
// create 后面接个泛型 <CounterState>,TS 马上就知道里面有什么
export const useCounterStore = create<CounterState>()((set, get) => ({
// 状态直接列出来,清晰明了!
n: 1, // 虽然接口里没定义这个,先放着(小声bb)
count: 0,
// 👇 这里我要自我检讨一下!
// 为了省事我用了 any... 大佬们轻喷
// set((state: any) => ...)
// 其实是因为我刚学 TS,有时候类型报错搞不定就用 any 大法保平安
// 大家千万别学我,后面我会改进的!
increment: () => set((state: any) => ({ count: state.count + 1 })),
decrement: () => set((state: any) => ({ count: state.count - 1 })),
// 这种直接重置的写法太舒服了,不用深拷贝什么的
reset: () => set({ count: 0 })
}));
小白心得 :
虽然代码里那一坨 any 有点辣眼睛,但你们看这个逻辑!没有 switch-case,没有 dispatch,就是简单的函数调用!这才是人类该写的代码啊!
第二关:Todo List + 持久化魔法
接下来的需求是做一个待办事项列表。这里我发现 Zustand 有个超级厉害的中间件叫 persist。
以前我要把数据存到 localStorage,得在 useEffect 里写好几行。现在?只要配置一行代码! 刷新页面数据居然真的还在,当时我就震惊了!😲
TypeScript
typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
// 先定义清楚我们的 Todo 长什么样,TS 的好处体现出来了
export interface Todo {
id: number,
title: string,
completed: boolean,
}
export interface TodoState {
todos: Todo[],
addTodo: (title: string) => void,
removeTodo: (id: number) => void,
toggleTodo: (id: number) => void,
}
// 这里的 <TodoState> 就像给代码装了导航仪
// 在写下面的 set 函数时,它会自动提示 todos 属性,太爽了
export const useTodoStore = create<TodoState>()(
persist(
(set, get) => ({
todos: [],
addTodo: (text: string) =>
set((state) => ({
// 这里的 ...state.todos 是不可变数据的写法
// 虽然有点绕,但为了 React 能更新视图,我忍了!
todos: [...state.todos, {
id: + Date.now(),
title: text,
completed: false,
}]
})),
toggleTodo: (id: number) =>
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ?
{...todo, completed: !todo.completed} // 反转状态
: todo
)
})),
removeTodo: (id: number) =>
set((state) => ({
todos: state.todos.filter(todo => todo.id !== id)
})),
}),
{
name: 'todos', // 👇 见证奇迹的时刻!只要这一行,自动存 LocalStorage
}
)
)
真香时刻 :
只要加上 persist 和 { name: 'todos' },剩下的脏活累活 Zustand 全包了。这体验,简直是从原始社会直接跨入现代文明!🌆
第三关:用户登录 & 接口规范
最后是用户模块。以前写 JS 的时候,经常不知道 user 对象里到底有 username 还是 userName,拼错单词 debug 半天。
现在配合 TS 的 interface,把规矩立在前面:
TypeScript
typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
// 定义用户长啥样
export interface User {
id: number,
username: string,
avatar?: string, // ? 表示头像可有可无
}
interface UserState {
isLoggin: boolean; // 虽然这里我想写 isLoggedIn,但不小心拼错了...
login: (user: { username: string; password: string }) => void;
logout: () => void;
user: User | null;
}
export const useUserStore = create<UserState>()(
persist(
(set) => ({
isLoggin: false,
// 登录逻辑简直到离谱,一行代码搞定状态切换
login: (user) => set({ isLoggin: true, user: null }),
logut: () => set({ isLoggin: false, user: null }),
user: null,
}),
{
name: 'user',
}
)
)
TS 初体验总结 :
虽然定义 interface User 和 UserState 确实要多写几行代码,但在TRAE里写代码时,那种敲一个点 . 就能自动弹出属性的感觉,真的太有安全感了! 再也不怕因为手滑写错单词而报错了。
结尾碎碎念
作为一个前端萌新,我觉得 Zustand + TypeScript 简直是绝配!
- Zustand 负责简单(拒绝样板代码)。
- TypeScript 负责安全(拒绝低级错误)。
如果你也像我一样被 Redux 折磨得痛不欲生,赶紧去试试 Zustand 吧!入股不亏!