10import { useStore } from './store'20import Number from './number'30import Flex from '@/components/ui/flex'4050export default function Counter() {60const count = useStore(state => state.count)70const inc = useStore(state => state.inc)80const dec = useStore(state => state.dec)9010return (11<div className='flex flex-col items-center space-y-4'>12<Flex center className='border border-gray-200 rounded size-48'>13<Number value={count} />14</Flex>15<div className='space-x-2'>16<button className='button' onClick={dec}>count--</button>17<button className='button' onClick={inc}>count++</button>18</div>19</div>20)21}
在全局状态管理器的选择上,React 给开发者出了一个很大的难题。
原因是由于 diff 更新的缘故,内置的 context 在性能的表现上并不友好,context 中状态的变化会导致频繁的 re-render。因此社区里纷纷在寻求新的解决方案,在这个过程中,迸发出了大量的状态管理方案。redux、mobx、zustand、recoil、jotai、Valtio、helux...
头晕,新人朋友冲进来,感觉人都麻了。
就算不是新人朋友,对于老手来说都是一个麻烦事,因为这对技术选型非常不友好,到底哪一种方案才是最好的?这需要大量的试错成本。每一种方案都有许多文章在鼓吹各种好,但是,具体到底谁更好?就没啥人能说出个一二三来。当然发展到现在,目前最主流的就是 zusntad 与 Jotai 之争
而其他基于 signal 设计的框架基本上就不会这方面的困扰,这些框架本身在底层逻辑上,就非常容易可以在确保高性能的前提之下做到全局状态共享,这也是 React 学习成本比较高的原因之一。
这本小册的主要目的,是花几篇文章,几个案例给大家分享一下 zustand
的使用,并对其有一个正确的理解,让读者朋友有充足的理由对其进行取舍判断。如果你已经使用了
10import { create } from 'zustand'2030type Store = {40count: number50inc: () => void60}7080export const useStore = create<Store>()((set) => ({90count: 1,10inc: () => set((state) => ({ count: state.count + 1 })),11}))
zustand
是一个小巧,灵活的状态管理库。虽然它可能不是我认为最好的选择,但是他在易用性与性能两个方面都做得还算比较好。因此在项目中使用是一个还算不错的决定。我之所以选择介绍它,是因为我在我的付费专栏**《Next.js 启动》**中使用到了它,希望借助这本专栏将它的使用心得分享给有需要的朋友。
zustand
性能表现zustand 的性能表现还算可以。但是,需要牺牲一定的易用性作为代价。
如下的方式在组件中从 store 中获取状态是最优秀的写法。
1export default function Counter() {2const { count } = useStore()3return (4...5)6}
但是这种写法的代价就是,store 中别的状态更新时,该组件也会被通知更新。因此,为了更好的性能表现,让当前组件不受到别的状态影响,我们必须这样写
1export default function Counter() {2const count = useStore(state => state.count)3return (4...5)6}
这种写法让我感到些许难受的地方在于,当我们在一个组件中使用多个状态时,代码的就开始不优雅了,当然,如果你能接受这样的写法,那问题也不是太大。
1export default function Counter() {2const count = useStore(state => state.count)3const userinfo = useStore(state => state.userinfo)4const inc = useStore(state => state.inc)56return (7...8)9}
具体的表现,我们会在后续的案例中进一步验证。
使用 zustand
来管理组件状态,我们可以更少的关心组件的 re-render
问题,但是也不能太过于掉以轻心,因为 zustand
在性能方面依然还存在一些情况会导致意外发生。
zustand
底层并没有基于 context 来实现,而是采用了另外一个冷门的 api, react 的外部状态存储 useSyncExternalStore
,因此,状态是否被共享就不再由 Provider 来决定。我们可以非常灵活的选择某个子组件来读取存储在 zustand
中的状态,而不需要被 Provider 限制。
zustand
的状态存储在组件外部。而不是顶层组件。这样的情况,使得 zustand
在灵活性上得到了一个比较大的提升。我们可以有更丰富的方式来组织代码结构。
zustand
基于 react hooks 来设计,他的使用习惯比较接近 react 的自定义 hook,因此如果你熟悉自定义 hook 的话,学会 zustand
的使用只需要一分钟。也许当你看到了文中的几个案例,你就已经掌握了它的基础使用
10import Count1 from './count1'20import Count2 from './count2'3040export default function Counter() {50return (60<div className='flex items-center justify-center space-x-4'>70<Count1 />80<Count2 />90</div>10)11}
这里就简单介绍一下,后续我们会通过更多的案例来学习理解 zustand