在 React v18 版本中,提供了新的状态管理 Hook useSyncExternalStore
。从命名上来看,意思是同步外部 Store 数据源到 React 状态。从 React 文档中,还可了解到这个 Hook 支持 Concurrent 时间分片的特性,还另外标注了一点:
The Hook is provided for library authors to integrate libraries deeply into the React model, and are not typically used in application code.
意思是这个 Hook 一般不是在业务开发内使用,主要是提供给第三方库的开发者,用于移植它们的数据源到 React state。
本文将结合几个例子,解释在 **CSR(客户端渲染)**的环境下如何使用此 Hook 同步数据。
首先,您将定义一个 createStore
函数,用于创建 Store 并导出操作状态的相关操作方法。然后使用这个函数创建 Store 并传入用于初始化的对象。
const createStore = (initialState) => {
let currentState = initialState
return {
getState: () => currentState,
setState: (newState) => (currentState = newState)
}
}
const store = createStore({
value1: 1,
value2: 2
})
接下来,您将需要创建用于展示状态的组件 BasicView
,这个组件接收一个 stateKey
参数,即在状态对象中的键名。
const BasicView = ({ stateKey }) => {
return (
<div>
<h2>{stateKey} : {store.getState()[stateKey]}</h2>
</div>
)
}
const App = () => {
return (
<>
<BasicView stateKey='value1' />
<BasicView stateKey='value2' />
</>
)
}
您现在就可以在浏览器中,查看状态的内容了。
在 BasicView
组件内,另外添加一个按钮元素,并绑定点击事件,在每次点击时将 stateKey
对应的状态自增 1
const BasicView = ({ stateKey }) => {
const onAddState = () => {
const currentState = store.getState()
console.log('triggered', currentState[stateKey])
currentState[stateKey] += 1
store.setState(currentState)
}
return (
<div>
<h2>{stateKey} : {store.getState()[stateKey]}</h2>
<button onClick={onAddState}>add {stateKey}</button>
</div>
)
}
如果您点击了按钮,会发现内容毫无变化,但是 Store 里的状态对象的确发生了变化。这个原因很简单,因为您没有使用 useState
钩子 schedule 一个更新任务。
您将需要对 BasicView
和 createStore
进行改造,并增加一个 useStore
的自定义 hook,使得 Store 内部状态发生变化时,能够自动 schedule 更新任务。
const createStore = (initialState) => {
let currentState = initialState
const listeners = new Set()
return {
getState: () => currentState,
setState: (newState) => {
currentState = newState
listeners.forEach(fn => fn(newState))
},
subscribe: fn => {
listeners.add(fn)
return () => listeners.delete(fn)
}
}
}
const useStore = () => {
const [state, setState] = useState(store.getState())
useEffect(() => {
return store.subscribe(setState)
}, [])
return state
}
const BasicView = ({ stateKey }) => {
const storeState = useStore()
const onAddState = () => {
storeState[stateKey] += 1
store.setState({ ...storeState })
}
return (
<div>
<h2>{stateKey} : {storeState[stateKey]}</h2>
<button onClick={onAddState}>add {stateKey}</button>
</div>
)
}
createStore
新增了 subscribe
方法,注册状态发生变化的监听器,并返回一个用于取消监听的函数。监听器在 setState
被调用时触发。useStore
hook 创建了一个 state 和一个副作用,在副作用内使用 subscribe
方法监听 Store 状态变化,用于 schedule 更新任务