在一个自定义钩子中,我忘了将我的setState
调用 Package 在一个useEffect
中,这在React 18之前没有任何问题,但是现在不起作用了。如果存储更改回其初始值,组件不会重新呈现。我猜这可能是意料之中的,但是没有任何警告,所以很难发现。
所以这更像是一个问题:
- 这种行为是错误吗?
- 如果不是,那么警告另一个犯同样错误的开发人员是否有用?
React版本:18
重现步骤
1.让组件使用useSyncExternalStore
(例如redux 8)
1.在useEffect
或事件行程常式(在转译中)之外呼叫setState
1.来回触发对存储的更改
链接到代码示例:https://codesandbox.io/s/react-18-redux-8-reproduce-bug-ojdx43的最大值
当前行为
- 如果存储值更新为与其初始值不同的值,则组件将重新呈现
- 如果存储区再次更新为初始值,组件不会重新呈现
预期行为
- 如果存储值更新为与其初始值不同的值,则组件将重新呈现
- 如果存储区再次更新为初始值,组件*也会重新呈现
继续挖掘
当我试图追踪这个问题时,我发现在useEffect
外部调用的setState
会扰乱纤程更新队列,特别是之前的pushEffect
应该用最新的值调用updateStoreInstance
以更新内部缓存示例。由于setState
未 Package ,它根本没有被调用(取消?覆盖?)。
因此,缓存的示例永远不会更新,除了初始值之外的任何其他值都会正确地触发重新呈现,因为checkIfSnapshotChanged
最终总是与初始值进行比较。
4条答案
按热度按时间aydmsdu91#
为了熟悉react代码库,我写了一个小测试,在这个模式上失败了。(即使是预期的那样)
以下是fwiw(单位:
packages/react-reconciler/src/__tests__/useSyncExternalStore-test.js
):最后一个判断提示会因
setSameValue
行而失败,并在没有的情况下通过。nc1teljy2#
我不明白的是,在
renderWithHooks
中,有以下代码块:如果在render中调用了
setState
,它就会运行。然后,它会再次调用组件函数--但要这样做,它会重置workInProgress
的状态,包括updateQueue
。IIUC这会丢弃之前钩子推送的效果,而不刷新它们?这就是为什么
useSyncExternalStore
效果更新存储值不运行的原因,在这种情况下。事实上,有代码写来管理
setState
调用呈现,似乎承认这是一个合法的用例?我一定是错过了一些东西😅,如何确保这些效果运行,即使组件功能被再次调用之前,工作结束?
tct7dpnv3#
是的,在函数组件的主体中调用
setState()
是法律的的,只要你有条件地这样做:i5desfxk4#
好的,根据我之前的评论,IIUC,当状态在渲染阶段被分派时,丢弃正在进行的工作钩子是预期的。正在进行的工作队列应该被重新创建(以反映新的状态)。
useSyncExternalStore
使用在制品memoizedState
来比较先前的值,但在第二次呼叫的情况下,这已经包含新值,因此无法达成目的。当优先使用
currentHook
时,它可以工作。请参阅附带的PR和修复程序,让我知道你的想法!