又一知名前端庫停止維護...
最近,GitHub 上擁有 20k Star 的 React 狀態管理庫 Recoil 正式停止維護,其 GitHub 倉庫已被歸檔。Recoil 由 Meta 公司開源,然而,值得注意的是,Meta 已解雇 Recoil 團隊的所有成員,且該庫已有接近兩年的時間未進行更新,因此其停止維護似乎已成定局。
圖片
在 2025 年的當下,提到 React 狀態管理,我依舊首推 Zustand,它比 Redux、Mobx 的思維模型更簡單,沒有那么復雜的樣板代碼要寫。下面就來簡單看看 Zustand 的用法。
Zustand 是什么?
Zustand 是一個小型、快速且可擴展的狀態管理解決方案,基于簡化的 Flux 原則,使用基于 Hooks 的 API,不包含樣板代碼且不具有強制性。Zustand 在 Github 擁有近 50k Star,其 npm 每周下載量近 500w。
Zustand 的特點如下:
- 簡單性:與 Redux 相比,Zustand 更簡單且不具有強制性,不需要像 React-Redux 那樣使用 Context Provider 包裹應用。
- 基于 Hooks:提供了直觀易用的 Hooks 接口,讓開發者可以輕松地與狀態進行交互,減少樣板代碼。
- 單一數據源:整個應用的狀態被集中存儲在一個 store 中,該 store 可以被分割成多個狀態切片,每個切片負責一部分應用邏輯。
- 不可變性:狀態更新是不可變的,更新狀態時需要創建一個新的狀態對象,而不是直接修改現有狀態,從而簡化狀態管理并防止常見的可變性相關錯誤。
- 訂閱和選擇性響應性:組件可以訂閱特定的狀態切片,并在這些切片發生變化時自動重新渲染。
- 細粒度依賴跟蹤:使用代理實現對狀態變化的細粒度跟蹤,確保只有當相關狀態發生變更時才會觸發組件的重新渲染,最大限度減少了不必要的更新。
Zustand 的使用
基本使用
安裝 Zustand:首先,使用 npm 來安裝 Zustand:
npm install zustand
創建 Store:在 Zustand 中,Store是通過create函數創建的。每個Store都包含狀態和處理狀態的函數。
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0, // 初始狀態
increment: () => set((state) => ({ count: state.count + 1 })), // 增加count的函數
decrement: () => set((state) => ({ count: state.count - 1 })), // 減少count的函數
}));
create函數接受一個回調函數,該回調函數接受一個set函數作為參數,用于更新狀態。在這個回調函數中,定義了一個count狀態和兩個更新函數increment和decrement。
使用 Store:在組件中,可以使用自定義的 Hooks(上面的useStore)來獲取狀態和更新函數,并在組件中使用它們。
import React from 'react';
import { useStore } from './store';
function Counter() {
const { count, increment, decrement } = useStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
下面是一個較復雜的狀態管理:
import create from 'zustand';
const useUserStore = create((set) => ({
user: {
name: '',
age: 0,
email: ''
},
setUserName: (newName) => set((state) => ({ user: {...state.user, name: newName } })),
setUserAge: (newAge) => set((state) => ({ user: {...state.user, age: newAge } })),
setUserEmail: (newEmail) => set((state) => ({ user: {...state.user, email: newEmail } }))
}));
訂閱特定狀態片段
在 Zustand 中,如果有一個包含多個狀態的store,但在組件中只需要訂閱其中一個狀態,可以通過解構賦值從useStore返回的完整狀態對象中提取需要的狀態。Zustand的智能選擇器功能允許這樣做,而不會導致不必要的重新渲染。下面來看個簡單的例子。
在這個store中,有兩個狀態:count和name,以及兩個更新這些狀態的函數。
// store.js
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
name: 'Zustand Store',
increment: () => set((state) => ({ count: state.count + 1 })),
setName: (newName) => set({ name: newName }),
}));
export default useStore;
現在,在組件中,如果只想訂閱count狀態,可以這樣做:
// MyComponent.js
import React from 'react';
import useStore from './store';
function MyComponent() {
// 使用解構賦值從store狀態中提取count
const { count } = useStore((state) => ({ count: state.count }));
return (
<div>
<p>Count: {count}</p>
</div>
);
}
export default MyComponent;
在組件中,傳遞了一個選擇器函數給useStore。這個選擇器函數接受當前的store狀態作為參數,并返回需要的部分狀態(在這個例子中是count)。這樣,Zustand就知道只需要在count狀態變化時通知這個組件。
如果想要訂閱多個狀態,但不想訂閱全部狀態,可以在選擇器函數中返回多個狀態:
const { count, name } = useStore((state) => ({ count: state.count, name: state.name }));
使用中間件
Zustand 支持中間件,可以通過中間件來擴展其功能。例如,可以使用內置的persist中間件將狀態保存到本地存儲,或者使用devtools中間件在瀏覽器的 Redux DevTools 擴展中查看和調試 Zustand store 的狀態變化等。
在下面的例子中,使用persist中間件將count狀態保存到瀏覽器的 localStorage 中:
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useCounterStore = create(
persist(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'counter-storage', // 本地存儲的key
}
)
);
我們還可以根據需求來自定義中間件,格式如下:
const myCustomMiddleware = (config) => (set, get, api) => {
// 預處理邏輯
const result = config(set, get, api); // 調用原始配置
// 后處理邏輯
return result;
};
然后,可以像這樣來應用自定義中間件:
import { create } from 'zustand';
const createStore = (set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
});
const useStore = create(myCustomMiddleware(createStore));
export default useStore;
如果想同時應用多個中間件,可以直接將它們鏈式調用:
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
const useStore = create(
devtools(persist(createStore, { name: 'counter-storage' }), { name: 'my-counter-store' })
);
export default useStore;
異步支持
Zustand 默認支持異步操作,最直接的方式是在創建 store 的時候定義異步函數。可以像定義同步動作一樣定義異步動作,只需確保它們返回 Promise。
import { create } from 'zustand';
const useStore = create((set) => ({
data: null,
loading: false,
error: null,
fetchData: async () => {
set({ loading: true, error: null }); // 設置加載狀態
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
set({ data, loading: false }); // 更新狀態為成功
} catch (err) {
set({ error: err.message, loading: false }); // 更新狀態為失敗
}
},
}));
export default useStore;