学习useSyncExternalStore

129 阅读2分钟

目标

useSyncExternalStore允许开发者订阅一个外部(与state、context相比)的数据源,并在需要的时候使订阅的组件重新渲染.如文档中的例子,可以订阅来自window的事件,并在网络状况变更时更新组件.

用法

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

subscribe

形如

function subscribe(callback){
    setup(callback)
    return ()=>{
        cleanup(callback)
    }
}

callback是来自react的回调函数,可以使组件进行重新渲染.它应该返回一个清理函数,解除对这一数据源的订阅.

getSnapshot

从数据源中获取值的函数.在react认为store不变时,即没有调用subscribe接受的回调函数时,getSnapshot的返回值应当相同.

getServerSnapshot

这是一个可选参数.在服务端进行渲染或者客户端进行数据水合时会调用这个函数.它的返回值序列化后在客户端和服务端间应该是相同的.如果没有这个参数,那么组件只能在客户端渲染.

注意事项

subscribe

subscribe变化后,react会调用清理函数并用新的subscribe进行订阅.如果不需要重新订阅,subscribe应当是不变的.反之如果需要重新订阅,则应该使函数变化,所以这里使用useMemoizedFn可能会导致问题,因为没有调用清理函数.

getSnapshot的返回值

getSnapshot的返回值变化也会导致重新渲染(但不会重新订阅).在通知react数据源变动前,getSnapshot应当返回相同的值.这里的相同指的是Object.is代表的相同,可以保存上一次调用的快照以供调用

有什么意义

曾经redux使组件发起更新的方法类似于setState({}),和这个钩子相比在功能上似乎并没有不同.但实际上这个钩子是与react18的并发机制有关.
在并发模式下,react每个fiber独立指定渲染任务,这些任务相互独立且可以被取消和重复执行.如果只是简单的运用setState发起更新,订阅数据源的组件组件的更新时间可能各不相同,这可能会导致一些问题(例如,点击事件中使用了过期的数据).而useSyncExternalStore发起的更新总是同步的,订阅统一数据源的组件总会在统一时刻更新.

拓展: 什么是外部数据源?

这听上去意味着数据库、websocket一类的东西,但实际上,任何react调度之外的都可以被视作'外部数据源',开发者可以自定义规则进行组件更新(比如redux).