關於狀態管理
在介紹Zustand之前,要來講講「狀態管理」是什麼?
現行網頁越來越複雜,也有了前端與後端的分別,其中前端比較偏向畫面呈現、UI \ UX、使用者流程、不同裝置有無跑版...等等。後端比較偏功能上的實作,包含資料庫管理、登入、註冊...等等。
那麼針對前端越來越複雜,也開始有了框架的概念,而現行流行的三大框架,分別為React、Vue、Angular。不同框架也會有不同的「狀態管理」方式,例如:
React :基本上只需要管理資料,並且由「狀態」來去渲染
Vue:有資料與元件的「雙向綁定」
而基於React這個框架而言,又可以大致上可以分為
Local State
Context
Third Party
看到這邊應該可以理解,光是一個React框架中的「狀態管理」就已經相當複雜了。
因此這篇只會針對Zustand來進行講解,也會用實例來進行介紹。
我們也可以透過以下的套件下載總量對比來知道,過去一年中Redux、Mobx、Zustand盛行程度。
https://npmtrends.com/mobx-vs-react-redux-vs-zustand
Zustand 是什麼?
根據Zustand官方說法,這是一個輕量、快速,基於Flux以及Hook概念出現的「狀態管理」套件。
p.s. 上面那隻熊是Zustand標誌性的吉祥物!
其中Zustand 也是為了解決一些複雜的React狀態管理問題,例如:Zombie Child Problem、React Concurrency、Context Loss。
如果想要更了解這些問題是什麼的話,可以參考以下連結
我們也可以透過Zustand官方知道他們想要擊敗Redux的野心
Zustand用法
下面兩個簡單的用法,是針對JavaScript的使用者。
建立第一個Store
Zustand是基於Hook建立的,因此第一個建立的就是由Custom Hook建立的Store裡面可以放入包含變數、物件、函數。並且可以透過Set、Get來去做資料的「狀態管理」
import create from 'zustand'
const useBearStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
針對元件進行綁定
因為是使用Hook,因此在任何元件中皆可以直接載入做使用。也可以針對這些狀態進行渲染,可以看到短短的幾行就可以處理完成,算是Zustand中的一大福音。
function BearCounter() {
const bears = useBearStore((state) => state.bears)
return <h1>{bears} around here ...</h1>
}
function Controls() {
const increasePopulation = useBearStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>one up</button>
}
這邊也針對TypeScript使用者提供簡單的Zustand範例
Zustand TypeScript 寫法
在Zustand 我們會使用create來去建立新的store。透過函數傳入,透過Hook來回傳狀態,並且進行再次渲染。
import create from "zustand"
type Product = { sku: string; name: string; image: string }
type CartState = {
products: Product[]
cart: { [sku: string]: number }
addToCart: (sku: string) => void
removeFromCart: (sku: string) => void
}
// Selectors
// ...
// Initialize our store with initial values and actions to mutate the state
export const useCart = create<CartState>(set => ({
products: [
// ...
],
cart: {},
// Actions
// ...
}))
這邊也另外展示Redux以及Mobx的寫法,讀者也可以比較其差異,不過這篇不會針對Redux或是Mobx進行講解,只會針對Zustand以及其狀態管理進行講解
Redux TypeScript 寫法
import { createSlice, configureStore, PayloadAction } from "@reduxjs/toolkit"
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
// Slices
// Definee the shape of the state and how to mutate it
type ICart = { [sku: string]: number }
const cartInitialState: ICart = {}
const cartSlice = createSlice({
name: "cart",
initialState: cartInitialState,
reducers: {
// ...
},
})
type IProduct = { sku: string; name: string; image: string }
const productsInitialState: IProduct[] = [
// ...
]
const productsSlice = createSlice({
name: "products",
initialState: productsInitialState,
reducers: {},
})
// Actions
// ...
// Selectors
// ...
// Store
export const store = configureStore({
reducer: {
cart: cartSlice.reducer,
products: productsSlice.reducer,
},
})
// ...
const App = () => {
return (
<Provider store={store}>
<NavigationContainer>
<Stack.Navigator>{/* ... */}</Stack.Navigator>
<StatusBar style="auto" />
</NavigationContainer>
</Provider>
)
}
Mobx TypeScript寫法
import { types, Instance } from "mobx-state-tree"
const Product = types.model({
sku: types.string,
name: types.string,
image: types.string,
})
// Model and type our data with mobx state tree
const CartStore = types
.model("CartStore", {
products: types.array(Product),
cart: types.map(types.number),
})
// Actions to mutate the state
.actions(store => ({
// ...
}))
// Views are like selectors
.views(self => ({
// ...
}))
type CartStoreType = Instance<typeof CartStore>
// Spin up a hook to use our store and provide initial values to it
let _cartStore: CartStoreType
export const useCart = () => {
if (!_cartStore) {
_cartStore = CartStore.create({
products: [
// ...
],
cart: {},
})
}
return _cartStore
}
Zustand是否能超越Context以及Redux呢?
https://github.com/pmndrs/zustand#why-zustand-over-redux
根據Zustand官方,列舉以上幾點,這邊也簡單講解一下
Redux
Redux本身是一個較為複雜的狀態管理工具,會有Actioins、View、State的流程
以下是Redux的官方資料流示意圖,也可以知道如果要從無到有使用Redux的狀態管理工具學習門檻是比較高的。
https://dev.to/oahehc/redux-data-flow-and-react-component-life-cycle-11n
Context
Context 本身雖然是歸類在狀態管理,但實際上比較像是一個集中式觸發狀態的方式。
https://dev-yakuza.posstree.com/en/react/context-api/
比起Zustand而言是會發散在各個檔案,不好進行管理
結論
Zustand本身是一個滿容易學習的狀態管理工具,且自己在工作產品使用上也是相對方便的,包含針對狀態進行Set、Get或是在元件中進行使用都是相對方便,滿推薦給想針對React進行狀態管理的讀者。
感謝觀看~對於以上內容如有任何疑問或是想要討論的地方,都歡迎留言或是私訊我!