zustand状态管理器使用汇总

// 相关中间件等应用顺序(固定写法):create(immer(devtools(subscribeWithSelector(persist((set, get) => ({}))))))

1. 初步使用介绍

// 1. 安装zustand
npm install zustand

// 2. 创建store使用
import { create } from "zustand";
export const useBearStore = create((set) => ({
  bears: 0, 
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), 
  removeAllBears: () => set({ bears: 0 }),
}));

// 3. 组件内使用
import {useBearStore} from './store/index.js';

function App() {
  // 不建议的写法:(除非状态都要使用到,否则建议使用下方写法,理由是会一直触发渲染导致性能差),如果一定要使用这种写法,参照下方标题selector和shallow去使用提高性能,何时使用selector和shallow下方将会提到
  // const {bears, increasePopulation} = useBearStore();

  // 支持写法,如果有很多状态则使用下方标题selector和shallow,何时使用selector和shallow下方将会提到
  const bears = useBearStore(state => state.bears);
  const increasePopulation = useBearStore(state => state.increasePopulation);

  const handleClick = () => {
    increasePopulation();
  }
  return (
    <div className="App">
      {bears}
      <button onClick={handleClick}>按钮</button>
    </div>
  );
}

export default App;



2. 作者建议store的写法

1. store
import { create } from "zustand";

const initialState = {
    bears: 0,
    count: 0,
}
export const useBearStore = create(() => initialState)

export const increaseBear = () => useBearStore.setState((state) => ({ bears: state.bears + 1 }))
export const increaseCount = () => useBearStore.setState((state) => ({ count: state.count + 1 }))
export const decreaseCount = () => useBearStore.setState((state) => ({ count: state.count - 1 }))

2. 组件

import { useBearStore, increaseBear, increaseCount, decreaseCount } from './store/index.js';
import { shallow } from 'zustand/shallow';

// 子组件
function Child() {

  const { bears, count } = useBearStore(state => ({
    bears: state.bears,
    count: state.count,
  }), shallow);

  return (
    <div>
      <div>{bears}</div>
      <div>{count}</div>
      <button onClick={increaseBear}>bears+1</button>
      <button onClick={increaseCount}>count+1</button>
      <button onClick={decreaseCount}>count-1</button>
    </div>
  )
}

// 父组件, 一定要把页面的每个部位都封装成一个子组件,否则会导致失效性能一样差
function App() {
  return (
    <div className="App">
      <Child />
    </div>
  );
}

export default App;

3. zustand内get与set使用

// 1. store
import { create } from "zustand";
export const useBearStore = create((set, get) => ({
    bears: 0,
    count: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    increaseCount: () => set((state) => ({ count: state.count + 1 })),
    removeAllBears: () => set({ bears: 0 }),
    total: () => {
        // get()可以获取到state状态里的数据
        return get().bears + get().count
    }
}));

// 2. 组件使用

import { useBearStore } from './store/index.js';

function App() {
  const bears = useBearStore(state => state.bears);
  const count = useBearStore(state => state.count);
  // 使用set使bears+1
  const increasePopulation = useBearStore(state => state.increasePopulation);
  // 使用set使count+1
  const increaseCount = useBearStore(state => state.increaseCount);
  // 使用get获取bears+count
  const total = useBearStore(state => state.total);


  const handleClickBears = () => {
    increasePopulation();
  }
  const handleClickCount = () => {
    increaseCount();
  }

  return (
    <div className="App">
      <div>{bears}</div>
      <div>{count}</div>
      <button onClick={handleClickBears}>按钮bears</button>
      <button onClick={handleClickCount}>按钮count</button>
      {/* 汇总 */}
      <div>{total()}</div>
    </div>
  );
}

export default App;


4. selector使用(简单或单状态可以使用这个,多状态建议使用第四个shallow写法)

// 1. 创建一个ts文件
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
}

export default createSelectors;

// 2. store使用
import { create } from "zustand";
import createSelectors from './index.ts';

// 在store内使用
export const useBearStore = createSelectors(create((set, get) => ({
    bears: 0,
    count: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    increaseCount: () => set((state) => ({ count: state.count + 1 })),
    removeAllBears: () => set({ bears: 0 }),
    total: () => {
        // get()可以获取到state状态里的数据
        return get().bears + get().count
    }
})));

// 3. 组件内使用,提高性能
import { useBearStore } from './store/index.js';

// 子组件
function Child() {
  const increasePopulation = useBearStore.use.increasePopulation();
  const increaseCount = useBearStore.use.increaseCount();

  return (
    <div>
      <div>{Math.random()}</div>
      <button onClick={increasePopulation}>按钮bears</button>
      <button onClick={increaseCount}>按钮count</button>
    </div>
  )
}

// 子组件
function Some() {
  const { count, bears } = useBearStore();

  return (
    <div className="App">
      <div>{Math.random()}</div>
      <div>{bears}</div>
      <div>{count}</div>
    </div>
  );
}

// 父组件, 一定要把页面的每个部位都封装成一个子组件,否则会导致失效性能一样差
function App() {
  return (
    <div className="App">
      <Some />
      <hr />
      <Child />
    </div>
  );
}

export default App;

5. shallow使用(zustand 最新的5版本废弃下面这种写法,如果是5的可以直接看下方第六条去使用)

import { useBearStore } from './store/index.js';
// shallow判断两次对象是否相等, 相等则不会重新渲染
import { shallow } from 'zustand/shallow'

// 子组件
function Child() {
  // 对象写法
  // const { increasePopulation, increaseCount } = useBearStore(state => ({
  //   increasePopulation: state.increasePopulation,
  //   increaseCount: state.increaseCount
  // }), shallow)
  
  // 数组写法
  const [increasePopulation, increaseCount] = useBearStore(state => ([
    state.increasePopulation,
    state.increaseCount
  ]), shallow)

  return (
    <div>
      <div>{Math.random()}</div>
      <button onClick={increasePopulation}>按钮bears</button>
      <button onClick={increaseCount}>按钮count</button>
    </div>
  )
}

// 子组件
function Some() {
  const { count, bears } = useBearStore();

  return (
    <div className="App">
      <div>{Math.random()}</div>
      <div>{bears}</div>
      <div>{count}</div>
    </div>
  );
}

// 父组件, 一定要把页面的每个部位都封装成一个子组件,否则会导致失效性能一样差
function App() {
  return (
    <div className="App">
      <Some />
      <Child />
    </div>
  );
}

export default App;

6. createWithEqualityFn(还在学习中~)

7. persist保存状态使用(相当于浏览器缓存)

1. store
import { create } from "zustand";
import { persist, createJSONStorage } from 'zustand/middleware'

export const useBearStore = create(persist(
    (set, get) => ({
        bears: 0,
        count: 0,
        increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
        increaseCount: () => set((state) => ({ count: state.count + 1 })),
        removeAllBears: () => set({ bears: 0 })
    }),
    {
        // 必须传配置项,name代表名称一定要唯一
        name: 'bear store',
        // 默认localStorage, 可以是sessionStorage,IndexDB或移动端asyncStorage,写法如下
        storage: createJSONStorage(() => localStorage),
        // 控制缓存你想要的状态
        partialize: state => ({ bears: state.bears, count: state.count }),
        // 过滤掉你不想要的状态:这个例子是除了count不要以外,其他状态都要在缓存内
        // partialize: state => Object.fromEntries(Object.entries(state).filter(([key]) => !['count'].includes(key)))
    }
))

2. 组件
import { useBearStore } from './store/index.js';
import { shallow } from 'zustand/shallow';

// 父组件, 一定要把页面的每个部位都封装成一个子组件,否则会导致失效性能一样差
function App() {
  const { count, bears, increasePopulation, increaseCount } = useBearStore(state => ({
    count: state.count,
    bears: state.bears,
    increasePopulation: state.increasePopulation,
    increaseCount: state.increaseCount,
  }), shallow);

  return (
    <div className="App">
      <div>{bears}</div>
      <div>{count}</div>
      <button onClick={increasePopulation}>按钮bears</button>
      <button onClick={increaseCount}>按钮count</button>
      {/* 清除缓存后再点击按钮会存在原来的数字加1,因为没清除状态 */}
      <button onClick={useBearStore.persist.clearStorage}>清除缓存</button>
    </div>
  );
}

export default App;

8. subscribe使用(发布与订阅,优点是在点击时如果此页面组件状态未改变不会触发重新渲染,用来提升性能),应用于状态少,状态多只关心其中一部分则使用第7个subscribeWithSelector

1. 
// store
import { create } from "zustand";

export const useBearStore = create((set, get) => ({
    bears: 0,
    count: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    increaseCount: () => set((state) => ({ count: state.count + 1 })),
    decreaseCount: () => set((state) => ({ count: state.count - 1 }))
}))

// 组件
import { useBearStore } from './store/index.js';
import { useEffect, useState } from 'react'
import { shallow } from 'zustand/shallow';

// 子组件
function Child() {
  const { increasePopulation, increaseCount, decreaseCount } = useBearStore(state => ({
    increasePopulation: state.increasePopulation,
    increaseCount: state.increaseCount,
    decreaseCount: state.decreaseCount,
  }), shallow);

  const [bg, setBg] = useState('white');
  useEffect(() => {
    // 添加订阅模式,监听到满足条件时才触发以及渲染组件, 否则不重新渲染组件
    const unsub = useBearStore.subscribe((state, prevState) => {
      // 如果count大于2则背景变红
      if (state.count > 2) {
        setBg('red')
      } else {
        setBg('white')
      }
    });

    //  unsub返回的是 () => listeners.delete(listener);
    return unsub
  }, [])

  return (
    <div style={{ backgroundColor: bg }}>
      <div>{Math.random()}</div>
      <button onClick={increasePopulation}>按钮bears</button>
      <button onClick={increaseCount}>按钮count + 1</button>
      <button onClick={decreaseCount}>按钮count - 1</button>
    </div>
  )
}

// 子组件
function Some() {
  const { count, bears } = useBearStore();

  return (
    <div className="App">
      <div>{Math.random()}</div>
      <div>{bears}</div>
      <div>{count}</div>
    </div>
  );
}

// 父组件, 一定要把页面的每个部位都封装成一个子组件,否则会导致失效性能一样差
function App() {
  return (
    <div className="App">
      <Some />
      <Child />
    </div>
  );
}

export default App;

9. subscribeWithSelector使用(假如你有很多状态,但你只关心其中一部分,可使用subscribeWithSelector)

// store
import { create } from "zustand";
import { subscribeWithSelector } from 'zustand/middleware'

export const useBearStore = create(subscribeWithSelector(
    (set, get) => ({
        bears: 0,
        count: 0,
        increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
        increaseCount: () => set((state) => ({ count: state.count + 1 })),
        decreaseCount: () => set((state) => ({ count: state.count - 1 }))
    })
))

// 组件

import { useBearStore } from './store/index.js';
import { useEffect, useState } from 'react'
import { shallow } from 'zustand/shallow';

// 子组件
function Child() {
  const { increasePopulation, increaseCount, decreaseCount } = useBearStore(state => ({
    increasePopulation: state.increasePopulation,
    increaseCount: state.increaseCount,
    decreaseCount: state.decreaseCount,
  }), shallow);

  const [bg, setBg] = useState('white');
  useEffect(() => {
    // // 添加订阅模式,监听到满足条件时才触发以及渲染组件, 否则不重新渲染组件
    // const unsub = useBearStore.subscribe((state, prevState) => {
    //   // 如果count大于2则背景变红
    //   if (state.count > 2) {
    //     setBg('red')
    //   } else {
    //     setBg('white')
    //   }
    // });

    // 使用中间件subscribeWithSelector写法
    const unsub = useBearStore.subscribe(state => state.count, (count, prevCount) => {
      if (count > 2) {
        setBg('red')
      } else {
        setBg('white')
      }
    }, {
      // 与先前的对象做比较是否相关,提升性能
      equalityFn: shallow,
      // subscribe是否立即执行
      fireImmediately: true,
    });
    return unsub
  }, [])

  return (
    <div style={{ backgroundColor: bg }}>
      <div>{Math.random()}</div>
      <button onClick={increasePopulation}>按钮bears</button>
      <button onClick={increaseCount}>按钮count + 1</button>
      <button onClick={decreaseCount}>按钮count - 1</button>
    </div>
  )
}

// 子组件
function Some() {
  const { count, bears } = useBearStore();

  return (
    <div className="App">
      <div>{Math.random()}</div>
      <div>{bears}</div>
      <div>{count}</div>
    </div>
  );
}

// 父组件, 一定要把页面的每个部位都封装成一个子组件,否则会导致失效性能一样差
function App() {
  return (
    <div className="App">
      <Some />
      <Child />
    </div>
  );
}

export default App;

10. 如何在组件内使用zustand的setState和getState直接更新或获取状态(性能也很好,不会导致不必要的渲染)

1. store
import { create } from "zustand";
// import { subscribeWithSelector } from 'zustand/middleware'

export const useBearStore = create((set, get) => ({
    bears: 0,
    count: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    increaseCount: () => set((state) => ({ count: state.count + 1 })),
    decreaseCount: () => set((state) => ({ count: state.count - 1 }))
}))

2. 组件

import { useBearStore } from './store/index.js';
import { useEffect, useState } from 'react'
import { shallow } from 'zustand/shallow';

// 子组件
function Child() {
  // 获取useBearStore的初始值bears,属于non-reactive
  console.log(useBearStore.getState().bears);

  const { bears } = useBearStore(state => ({
    bears: state.bears,
  }), shallow);

  // 直接在组件调用setState去更新而不需要在store内写
  const handleAddBearfive = () => {
    useBearStore.setState(state => ({ bears: state.bears + 5 }))
  }

  return (
    <div>
      <div>{bears}</div>
      <button onClick={handleAddBearfive}>按钮bears直接 + 5</button>
    </div>
  )
}

// 父组件, 一定要把页面的每个部位都封装成一个子组件,否则会导致失效性能一样差
function App() {
  return (
    <div className="App">
      <Child />
    </div>
  );
}

export default App;

11. zustand - 切片模式(单个store比较大的时候可以采用切片模式进行模块拆分组合,类似模块化)

import { create } from 'zustand';

const createCounterStore = (set) => {
    return {
        count: 0,
        inc: () => {
            set(state => ({ count: state.count + 1 }))
        }
    }
}

const createChannelStore = (set) => {
    return {
        channelList: [],
        fetchChannelList: async () => {
            const res = await fetch(URL);
            const jsonRes = res.json();
            set({
                channelList: jsonRes.data.channels
            })
        }
    }
}

const useStore = create((...params) => {
    return {
        ...createCounterStore(...params),
        ...createChannelStore(...params)
    }
})

function Layout() {
    const count = useStore(state => state.count);
    const inc = useStore(state => state.inc);

    return (
        <div>
            <button onClick={inc}>{count}</button>
        </div >
    )
}

export default Layout;

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值