原文链接:useGlobalState()
演示:useGlobalState Hooks - CodeSandbox
这篇文章介绍了一种简单的方法来共享React状态,而不需要使用context或复杂库。作者提供了一个自定义挂钩,使用全局变量来存储数据和监听器。使用这个挂钩,组件之间可以共享一个状态。文章还讨论了常见的解决方案,如使用prop drilling
和context
,以及这些方法存在的问题。最后,作者提供了一个包含示例的完整代码实现。
在React中因为状态流向和作用域的问题,无法在组件之间共享状态,状态只能向下传递,即是使用了自定义的 Hooks,并且在多个组件之间使用该 Hooks,每个组件也将获得自己的状态实例。
Props 下钻
最简单的做法是使用**Props
下钻**,也就是说将状态提升至最顶层的父级组件中,通过 Props
将状态传递给子组件,达到多个子组件共享状态的目的,这种方式存在一些问题
- 需要共享状态的组件必须拥有共同的父级
- 组件层级较深时,复杂度提高,加重心智负担
- 状态更新时,如果不处理好
memo
,将会造成多余的重新渲染
Context
React 引入了 Context 概念,用来处理状态共享和Props下钻时层级过深的问题,对于基本的情况来说,但是当复杂度进一步提升,存在多个 Context 时,就会出现很多的嵌套上下文
或许可以使用一个唯一的 Context
,但是这种方法存在的问题时,对于共享状态的任何更改,都会导致所有消费者的重新渲染。无法有选择的更新某些消费者,一旦更新了提供者的值,所有的消费者都会重新渲染,极大地影响性能。
另一个问题时,当共享状态需要变化时,必须更新提供者(公共父组件),不能只更新共享状态的子节点。如果子组件没有使用memo
或者PureComponent
进行适当优化,则会存在性能问题。
解决方案
使用观察者模式可以很好的解决这个问题,首先需要在全局变量中存储Subject
和Observers
,调用钩子时注册一个新的监听器, 任何组件调用Hook 的状态更新时,向所有的监听器发出信号。
为了支持多个状态,需要使用一个唯一标识符来标识状态,这里建议使用Symbol
,需要注意,唯一标识符不能再 useGlobalState
内部定义,因为每次调用时都会创建一个新的表示符,需要在全局定义一次。
使用方式与useState
一样
生成器&优化唯一标识符
每次使用 Hook 时都需要手动创建 Symbol
唯一标识符,比较麻烦,可以通过创建一个 生成器函数来淡化 Key 的概念,同时,将每个状态对应的监听器分开存放。
完整代码
使用
创建组件
在多个组件中使用同一个状态
配合 useSyncExternalStore 使用
useSyncExternalStore
是 React 18 的新钩子,可以在 React 内部和外部存储之间同步状态