在 useEffect 中响应取消订阅 RxJS 订阅

dax*_*cks 3 rxjs reactjs

关于 useEffect hooks 中 RxJS 的一个简单问题

我注意到 React 社区在 RxJS 中使用了这种取消订阅模式:

useEffect(()=>{
  const sub = interval(10).subscribe()
  return ()=>sub.unsubscribe()
})
Run Code Online (Sandbox Code Playgroud)

我很好奇这是否是由于约定/代码清晰度所致,或者我是否忽略了某些内容。我想下面的内容会更简单:

useEffect(()=> interval(10).subscribe().unsubscribe)
Run Code Online (Sandbox Code Playgroud)

然而,我可能忽略了一些事情。

编辑:查看所选答案。“This”绑定在方法调用上,而不是绑定在订阅实例化上。结果,取消订阅失败,因为“this”对象不是指间隔订阅,而是指 useEffect 回调环境。感谢两位贡献者。以下是 useEffect 挂钩失败的示例:codesandbox.io/s/morning-bush-7b3m6h?file=/src/App.js

Mrk*_*Sef 8

这里:

useEffect(()=>{
  const sub = interval(10).subscribe();
  return () => sub.unsubscribe();
});
Run Code Online (Sandbox Code Playgroud)

可以重写为:

useEffect(()=>{
  const sub = interval(10).subscribe();

  function unsub(){
    sub.unsubscribe();
  }

  return unsub;
});
Run Code Online (Sandbox Code Playgroud)

需要注意的关键是您将一个函数返回给 React。unsub不会立即调用,而是稍后在组件卸载时调用。

事实上,您可以返回任意代码以便稍后运行:

useEffect(() => {
  /****** 
   * Code that gets called when 
   * effect is run
   ******/ 
  return () => { // <- start of cleanup function 
    /****** 
     * Code that gets called to 
     * clean up the effect later
     ******/
  } // <- end of cleanup function
});
Run Code Online (Sandbox Code Playgroud)

问题

我将重写您的解决方案,以便更清楚地讨论问题。这在语义上是等价的,我刚刚引入了一个中间变量。

useEffect(() => 
  const sub = interval(10).subscribe();
  return sub.unsubscribe;
);
Run Code Online (Sandbox Code Playgroud)

最明显的问题归结为:这些值之间有什么区别?在哪种情况下(如果有的话)一个会失败而另一个不会。

sub.unsubscribe
() => sub.unsubscribe()
Run Code Online (Sandbox Code Playgroud)

如果 unsubscribe 是一个函数(未绑定到类/对象的实例,因为它不包含 this 关键字),则两者在语义上是等效的。

问题是取消订阅实际上并不是一个函数。它是订阅对象上的一个方法。因此,上面的第一个值是this未定义的未绑定方法。当该方法尝试使用其上下文 ( this) 时,JavaScript 将抛出错误。

为了确保它unsubscribe被作为方法调用,您可以这样做:

useEffect(() => {
  const sub = interval(10).subscribe();
  return sub.unsubscribe.bind(sub);
});
Run Code Online (Sandbox Code Playgroud)

尽管看起来大致相同,但这种方式会减少一级间接性。

此外,我建议bind在大多数情况下不要使用。方法、函数、匿名 lambda 函数以及包含这三个值中任何一个的属性在各种边缘情况下的行为都不同。

据我所知,() => a.b()可能不必要地包装了一个函数,但不会失败。另外,JIT 会在 99.9% 的情况下很好地优化这一点。

a.b.bind(a)以前绑定的方法会失败,但 100% 都会得到优化。除非有必要,否则我不会使用bind(而且很少有必要)

更新:

顺便说一句:我在这里使用函数来表示不依赖于上下文的可调用代码块(不具有使用关键字引用的对象this),并使用方法来表示不依赖于上下文的可调用代码块依赖于某些上下文。

如果您更喜欢其他术语,也没关系。当你读的时候交换一下单词,我不会生气,保证:)