zustand入门

介绍

  1. 安装
npm install zustand
  1. 创建与使用
    store是个hook,create函数的回调函数返回的值就是store,可以存放原始值,对象,函数(相当于action),其中回调函数的set参数默认是合并state
import { create } from 'zustand'

const useStore = create((set) => ({
  bears: 0,// 这里是state
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),// 函数相当于action
  removeAllBears: () => set({ bears: 0 }),
}))

组件中使用,useStore接受一个函数参数,该函数称为selector函数,参数返回值作为要使用的state值或action函数,当返回的值有变化时,使用到的组件才会更新,如果不传selector函数,那么只要state值发生变化,那么组件就会重新渲染

function BearCounter() {
  const bears = useStore((state) => state.bears)
  return <h1>{bears} around here...</h1>
}

function Controls() {
  const increasePopulation = useStore((state) => state.increasePopulation)
  return <button onClick={increasePopulation}>one up</button>
}

更新状态state

zustand通过set函数更新state,set函数通过接受一个返回state的函数,浅合并该state

浅合并

const useStore = create<State & Action>((set) => ({
  firstName: '',
  lastName: '',
  updateFirstName: (firstName) => set(() => ({ firstName: firstName })),
  updateLastName: (lastName) => set(() => ({ lastName: lastName })),
}))

嵌套对象

如果要更新嵌套深层的对象,那么需要层层解构赋值,比较麻烦。

  normalInc: () =>
    set((state) => ({
      deep: {
        ...state.deep,
        nested: {
          ...state.deep.nested,
          obj: {
            ...state.deep.nested.obj,
            count: state.deep.nested.obj.count + 1
          }
        }
      }
    })),

使用Immer库更新嵌套对象

使用immer库的produce函数将set函数的函数参数包裹起来,就更方便了

immerInc: () =>
    set(produce((state: State) => { ++state.deep.nested.obj.count })),

不可变的state和合并

默认情况下,zustandset函数会合并其函数参数返回的state,如果想禁用这一行为,将返回的state作为新state而不合并,给set函数传递第二个参数replaceFlag:true就行

set((state) => newState, true)

受 Flux 架构的启发

推荐的使用模式

  1. 创建单仓库store,应用过大可以将store分成几个 slice ,类似 vuex 里的 module
  2. 使用set,或者 setState 更新仓库 store
useStore.setState((state)=>({count:state.count+1}))
increment:()=>set((state)=>({count:state.count+1}))
  1. storeactions放在一起
const useBoundStore = create((set) => ({
  storeSliceA: ...,
  storeSliceB: ...,
  storeSliceC: ...,
  updateX: () => set(...),
  updateY: () => set(...),
}))

自动生成 selectors 函数

  1. 定义 createSelectors 函数
    selector 函数用于从store中返回需要的stateaction,每次都写一个这样的函数很繁琐,可以通过一个函数自动给useStore添加selector函数,该函数目的是在 useStore上添加use属性,通过useStore.use.xxx()来返回所需stateaction
import { StoreApi, UseBoundStore } from 'zustand'

type WithSelectors<S> = S extends { getState: () => infer T }
  ? S & { use: { [K in keyof T]: () => T[K] } }
  : never

const createSelectors = <S extends UseBoundStore<StoreApi<object>>>(
  _store: S
) => {
  let store = _store as WithSelectors<typeof _store>
  store.use = {}
  for (let k of Object.keys(store.getState())) {
    ;(store.use as any)[k] = () => store((s) => s[k as keyof typeof s])
  }

  return store
}
  1. 使用 createSelectors 包裹 useStore
    假如有这么一个 store ,使用 createSelectors 包裹 useStore
interface BearState {
  bears: number
  increase: (by: number) => void
  increment: () => void
}

const useBearStoreBase = create<BearState>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
  increment: () => set((state) => ({ bears: state.bears + 1 })),
}))

const useBearStore = createSelectors(useBearStoreBase)

通过 store.use.xxx(),获取stateaction

// get the property
const bears = useBearStore.use.bears()

// get the action
const increment = useBearStore.use.increment()

没有 action 的仓库如何更新state

使用 store.setState((state)=>{})

export const useBoundStore = create(() => ({
  count: 0,
  text: 'hello',
}))

export const inc = () =>
  useBoundStore.setState((state) => ({ count: state.count + 1 }))

export const setText = (text) => useBoundStore.setState({ text })

结合 typescript 使用

  1. 基础用法
    使用create<T>()()
create<State>()((set)=>({}))

import { create } from 'zustand'

interface BearState {
  bears: number
  increase: (by: number) => void
}

const useBearStore = create<BearState>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
}))

Map和Set的使用

更新MapSet需要新建一个MapSet,主要是为了让 React 知道更新发生了

import { create } from 'zustand'

const useFooBar = create(() => ({ foo: new Map(), bar: new Set() }))

function doSomething() {
  // doing something...

  // If you want to update some React component that uses `useFooBar`, you have to call setState
  // to let React know that an update happened.
  // Following React's best practices, you should create a new Map/Set when updating them:
  useFooBar.setState((prev) => ({
    foo: new Map(prev.foo).set('newKey', 'newValue'),
    bar: new Set(prev.bar).add('newKey'),
  }))
}

切片模式

  1. 用法
    类似 vuex 的modules,如果有同名的属性,但是后面的 slice 会覆盖前面的

import { create } from 'zustand'

export const createFishSlice = (set) => ({
  fishes: 0,
  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
})

export const createBearSlice = (set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
  eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),
})


export const useBoundStore = create((...a) => ({
  ...createBearSlice(...a),
  ...createFishSlice(...a),
}))

function App() {
  const bears = useBoundStore((state) => state.bears)
  const fishes = useBoundStore((state) => state.fishes)
  const addBear = useBoundStore((state) => state.addBear)
  return (
    <div>
      <h2>Number of bears: {bears}</h2>
      <h2>Number of fishes: {fishes}</h2>
      <button onClick={() => addBear()}>Add a bear</button>
    </div>
  )
}

export default App
  1. 使用中间件 persist
import { create } from 'zustand'
import { createBearSlice } from './bearSlice'
import { createFishSlice } from './fishSlice'
import { persist } from 'zustand/middleware'

export const useBoundStore = create(
  persist(
    (...a) => ({
      ...createBearSlice(...a),
      ...createFishSlice(...a),
    }),
    { name: 'bound-store' }
  )
)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值