Zustand是什麼?React前端狀態管理

2022/09/06閱讀時間約 12 分鐘
Zustand是什麼?React前端狀態管理
未來文章一併更新於此網站 Hogan.B Lab
並且包含多語系 繁體中文英文日文簡體中文
Zustand 吉祥物

關於狀態管理

在介紹Zustand之前,要來講講「狀態管理」是什麼?
現行網頁越來越複雜,也有了前端與後端的分別,其中前端比較偏向畫面呈現、UI \ UX、使用者流程、不同裝置有無跑版...等等。後端比較偏功能上的實作,包含資料庫管理、登入、註冊...等等。
那麼針對前端越來越複雜,也開始有了框架的概念,而現行流行的三大框架,分別為React、Vue、Angular。不同框架也會有不同的「狀態管理」方式,例如:
React :基本上只需要管理資料,並且由「狀態」來去渲染
Vue:有資料與元件的「雙向綁定」
而基於React這個框架而言,又可以大致上可以分為
Local State
  • useState
  • useReducer
Context
  • useContext
Third Party
  • Redux
  • Mobx
  • Zustand
看到這邊應該可以理解,光是一個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。
如果想要更了解這些問題是什麼的話,可以參考以下連結
Zombie Child Problem:React Redux 文件
React Concurrency:React 官方文件
React Context Loss:Stack Overflow
我們也可以透過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進行狀態管理的讀者。
感謝觀看~對於以上內容如有任何疑問或是想要討論的地方,都歡迎留言或是私訊我!
為什麼會看到廣告
希望能透過「React框架白話文運動」系列文章,利用口語化語表以及簡單的程式碼範例,能讓讀者更明白React的各種應用。 系列文章會講述以下: 1. 了解 ES6 JavaScript 語法 2. 了解 React 的運作原理 3. 了解 React 的狀態管理 4. 使用 React Hook管理狀態並且存取資料
留言0
查看全部
發表第一個留言支持創作者!