useSyncExternalStore
useSyncExternalStore 是一个 React Hooks,允许我们订阅外部 store 的 React Hook,详细请看 zh-hans.react.dev/reference/r…
一些 React 的全局状态库也是基于 useSyncExternalStore 开发的,例如 Zustand、Redux 8.0 等
实现思路
首先我们需要确认自己实现的全局状态管理的 api 调用:
- 首先创建一个
store
import { createStore } from '@plumbiu/react-store'
const countStore = createStore({
count: 1,
inc() {
return {
count: this.count + 1,
}
},
})
export default countStore
- 在组件中调用:
import countStore from './store'
import { useStore } from '@plumbiu/react-store'
function App() {
const store = useStore(countStore)
return <div>
<div>count: {store.count}</div>
<button onClick={data.inc}>inc</div>
</div>
}
- 书写代码
这里直接贴核心代码,代码都注释了:
import { useSyncExternalStore } from 'react'
// createStore
export function createStore(state) {
// 定义重新渲染的 callback
let listeners = []
// 执行重新渲染
function emitChange() {
listeners.forEach(fn => fn())
}
// 遍历我们定义的 state 属性,将函数重新赋值,以便执行 emitChange 方法触发重新渲染
for (const key in state) {
const fn = state[key]
if (typeof fn !== 'function') {
continue
}
state[key] = (action: any) => {
// 重新赋值
state = fn.call(state, action) ?? {}
// 赋值后调用 emitChange 重新渲染
emitChange()
}
}
// 返回的数据
return {
$subscribe(listener: Function) {
listeners = listeners.concat(listener)
return () => (listeners = listeners.filter((l) => l !== listener))
},
$getSnapshot() {
return state
},
}
}
// useStore 比较简单,就是 createStore 的返回值作为 useSyncExternalStore 的参数
export function useStore(store) {
return useSyncExternalStore(store.$subscribe, store.$getSnapshot)
}
最后
上面的代码是大致的代码,还有一些小问题,例如在给 state 重新赋值的时候,应该先浅比较一下数据有没有更改,还有类似避免全局订阅的情况
上述问题解决具体可看 @plumbiu/react-store 中的代码(求一波 star