React Store 中央情报局

54 阅读4分钟

使用QueryClinet集中管理请求和保存

配置queryKey,会在QueryClient中被以map键值对的形式存在

同一个queryKey的请求只会存在一个,然后他们的data放在Client中(类似于一个外部的Store)保存

保存Query到外部的 store

一个queryKey的请求只会存在一个,然后他们的data放在Client中(类似于一个外部的Store)保存

使用Provider将queryClient提供给子组件

在APP顶层,通过contextProvider将data注入给子组件,子组件通过useQuery配置和获取数据,useQuery内部或许封装了useContext的逻辑

useContext本身是可以触发更新的,但是由于client是一个对象用isObject总是返回true,引用一样,而且我们不希望一个queryData改变的时候使用了clientData 的组件全部刷新。所以ReactQuery为每一个 queryKey 的data配置了更新组件的方法

观察者对象触发更新

每次使用useQuery相当于为这个QueryKey注册了一个观察者Observer对象,当queryKey对应的data改变的时候,Observer触发React更新,触发React更新的方法如下

forceUpdate & useSyncExternalStore

forceUpdate() 触发一个强制的同步更新

但是在concurrent模式出现之后,forceUpdate()会出现撕裂问题

后续react18给出了官方解决方案,使用useSyncExternalStore来更新外部的store

image.png

React Context

使用React.useContext获取数据,使用Context.Provider传递数据

创建Provider

使用ContextProvider,React会为其生成一个Fiber节点。包含_currentValue等值

消费context

使用useContext或者使用Consumer的时候

  • 向上寻找并从context._currentValue中获取value值,context._currentValue是保存在ContextProvider 上的(是一个组件,会变成一个Fiber节点)
  • 同时context放入Fiber.dependencies,用于在context变化的时候触发**propagateContext向下DFS Fiber子树然后根据dependencies**为子节点标记lane,从而触发渲染流程
  • context的值是通过isObject来比较的,所以是比较引用

使用context触发的链路: 向上寻找contextProvider,获取context._currentValue,维护dependencies

改变context触发的链路: 触发propagateContext,执行DFSFiber树,根据dependencise标记lane,触发更新流程

看完了 reactQuery,我们再对比的来看一下 redux | zustand

订阅发布模式

redux中的数据本质上是一个js对象,我们称之为store。

创建一个store,store需要接受一些Reducer,reducer就是通过action触发state更新的函数

store中导出了实现发布订阅的方法 :

  • dispatch, dispatch(action) 用于触发一个更新,执行reducer函数出发state更新
  • subscribe,提供订阅功能,对应state发生变化的时候执行这个subscribe传入的回调参数
  • getState,从store中获取state,在react-redux中,获取state的时候同时集成了为这个state添加了一个subscribe的功能

react-redux != redux

redux中,我们需要自己手动subscribe,再subscribe中使用forceUpdate()触发更新,或者触发useSyncExternalStore() 触发并发更新

react-redux中我们不需要这样做,再调用getState的时候除了返回state还会添加订阅,自动完成了这一部分,同时通过context,我们也不需要再引入store

useSyncExternalStore钩子是在react18导入的, 而且他是不能够被类组件调用的,这是我发现的函数组件无法做到的类组件的事情

React-Redux 通过 context向下传递 store

  • 与ReactQuery这个库类似,React-Redux也是通过store + context将数据传送到子组件中,

  • 调用getState方法的时候,实际上react-redux搜索对应的context,从中获取state

  • 调用dispatch的时候,dispatch触发reuducer函数,reducer函数通过action执行相应的state修改操作同步到store,如何同步的?reducer 其实是从store中导出的属性,所以我们可以通过reducer修改store

    • state变化后会触发subscribe函数队列执行,subscribe会触发对应的组件更新 (通过forceUpdate或者useSyncExternalStore)

为什么context本身不引起更新?

context对象本身使用isObject判断是否数据发生变化,当时store中的数据是一个对象,他的引用没有改变,所以没有触发更新。这有利于我们做更细粒度的更新。

使用connect 高阶函数获取dispatch和state

导入connect方法,connect方法从store中获取dispatch和getState函数,通过高阶组件的形式传入给组件 笔者才疏学浅,各位读者多多担待,不吝赐教。部分插图来自网络,侵删。