Closed dragooncjw closed 2 years ago
useSubscription
在触发回调前会先检查 staleness ,从而避免调用旧的回调。所以即便在渲染阶段也是调用正确的回调。
其实我是通过useSubscription的observer来实现的,这里看源码是通过useRef实现的。如果我使用input$来实现我想要实现的功能的话,会因为闭包,导致我在函数内部调用的永远是init时候的值。 https://github.com/crimx/observable-hooks/blob/e29d60b9305ff9c1bc4d7ac225147ec2de6349be/packages/observable-hooks/src/internal/use-subscription-internal.ts#L42-L47
可以通过 codesandbox 构造一个例子说明你遇到的问题么?这里没看明白与 RxJS 有什么关系?
其实是我想要接触一下React 18的并发模式,但是项目中又有使用React useEventCallback
的场景,(https://reactjs.org/docs/hooks-faq.html)
因此想要找到一种方法,避免useEventCallback
实现中的useRef导致的并发模式下的问题。我理解RxJS的订阅发布机制可以解决这个问题,但是如果我使用input$来实现,这就意味着我需要将所有依赖的数据全部转化为Observable,这其实并不符合预期,且工作量过大。
下面是React官网 useEventCallback
的实现源码:
function useEventCallback(fn, dependencies) {
const ref = useRef(() => {
throw new Error('Cannot call an event handler while rendering.');
});
useEffect(() => {
ref.current = fn;
}, [fn, ...dependencies]);
return useCallback(() => {
const fn = ref.current;
return fn();
}, [ref]);
}
由于使用useRef记住了fn,因此fn会更新,但是useEventCallback
包裹的fn的地址并不会更新。
但是这样的做法React官方不推荐,官网上是这么讲的:
我想找个替代方案,是否可以通过observable-hooks实现一套新的useEventCallback
,可以使我们记录函数内部最新的状态,同时遵从React官方文档的意愿,避免使用useEventCallback
我简单写了一个codesandbox解释我使用input$会存在的问题: https://codesandbox.io/s/context-with-and-without-usememo-with-memo-forked-u26d3?file=/src/ProviderWithMemo.js 我的ProviderWithMemo组件中点击Increment按钮的时候,console内打印出来的在App.js内的值永远是0,而ProviderWithoutMemo内的值永远是最新的.
这是因为你的 test
放在了 useObservableCallback
的 init
回调中,文档已经说明 init
回调只会执行一遍。另外见注意事项。
const [onChangeTest, textChange$] = useObservableCallback((event$) =>
event$.pipe(
tap(() => {
test("withMemo");
setCounter((c) => c + 1);
})
)
);
tap
是反 Rx 响应式模式所以一遍不推荐使用。
副作用放在 useSubscription
即可。
import { useObservableCallback, useSubscription, identity } from "observable-hooks";
const [onChangeTest, textChange$] = useObservableCallback(identity);
useSubscription(textChange$, () => {
test("withMemo");
setCounter((c) => c + 1);
});
是的,我一开始的问题里也写了我是使用了useSubscription
里的observer实现解决的,但是问题在于我发现useSubscription
源码里对observer内数据的同步还是通过使用useRef
记录的,其实原理和使用useEventCallback
是一样的,所以感觉使用您上一个回复中的方法并不能避免React官方文档中useEventCallback
可能会出现的问题。
啊,是我记错了。之前举例的代码是用于检查 observable 更新的 staleness,不是检查回调的 staleness。
这个确实是与 useEventCallback
一样的,之前尝试过在 render 阶段赋值给 ref 但是因为有副作用会导致 React devtools 出错。
这个在 Observable 中可以接受是因为业务上限制了这个可能性。useSubscription
用于副作用的时候对于这个时机要求不敏感,用于 state 的时候因为 setState 都是一样的所以也不会出现问题。
以后observable-hooks会推出相关的解决方案吗?我正在寻求useEventCallback
的替代方案,但是没有找到好的解决方法
React 选择走上了函数组件+并行模式这条路决定了它不能在渲染阶段写带副作用的代码,所以 useEventCallback
这种模式只能通过业务设计上来规避,要么保证回调不会在渲染阶段触发,要么回调中不处理强依赖上下文的业务。而两种情况都需要的业务也非常少,通常都是反 React 模式的使用方式。
React 团队其实也知道这么结合代码不好写,所以拖了这么久 18 也没全上并行模式。
感谢大佬指点
下图是react官网对
useEventCallback
的描述,文档里表示在任何情况下都不建议使用这个函数, 我想使用observable-hooks库尝试实现替换react原生不被推荐的useEventCallback
。但是看了源码,发现
useSubscription
内的observer
也是使用useRef
实现的,那这样还是会存在渲染阶段的问题,我数据获取的时候只会获取到useRef初始化地方的变量,如果渲染阶段,在useRef后变量更新了,或者是在React并发模式下,还是会存在和useEventCallback
一样的问题。我如果将所有依赖都切换成
Observable
感觉处理起来会很麻烦,请问官方是否有合理的替代方案呢?下面是我的代码:
我将传给
useEventCallback
的函数替换成了usePersistCallback
内的observer。