首页 > 解决方案 > 在传递给 RxJS 订阅的回调中使用来自 useState 钩子的 React setState 会导致异常

问题描述

我正在编写一些帮助程序来简化 React 中 RxJS 的使用。

我编写了一个自定义钩子,它允许您发送回调以订阅并从 RxJS 流中获取更新并更新它。这个钩子还允许您为流错误和完成注册回调。这些在测试中正常工作,但是在编写适当的示例时,React 会在流错误上抛出错误,应该处理的错误以及在 UI 中显示的消息。

这是一个与下面代码相同的工作沙箱,带有用于更新、发送错误和完成流的按钮

演示组件如下。请注意发生错误的行:

export default ({subject})=>{
    const [hasError, setHasError] = useState(false);
    const [complete, setComplete] = useState(false);
    const onError = (err)=>{
        setHasError(true); // <-------------- Commenting this line avoids the error!
        console.log('Value handled error',err);
    };
    const onComplete = ()=>{
        setComplete(true);
        console.log('Value handled completion');
    };
    const [val,setVal] = useSubject(subject,onError,onComplete);
    const onClick = ()=>setVal(val+1);
    return (
        <span>
            {val}
            {hasError?' An Error ocurred ':null}
            {complete?' Stream has completed ':null}
            <button onClick={onClick}>increment</button>
            <button onClick={()=>subject.error(5)}>Generate error</button>
            <button onClick={()=>subject.complete()}>Complete</button>
        </span>
    );
}

自定义钩子如下:

export const useSubject = (subject,onError,onComplete) => {
  const [value, setValue] = useState(subject.getValue());
  useEffect(() => {
    const subFn = { next: data => setValue(data) };
    if (onError) { subFn.error = err => onError(err); }
    if (onComplete) { subFn.complete = () => onComplete(); }
    const sub = subject.pipe(skip(1)).subscribe(subFn);  
    return () => sub.unsubscribe();
  });
  const newSetState = state => subject.next(state);
  return [value, newSetState];
};

来自 React 的错误消息不是很有用,如果我理解正确,错误边界只会帮助我显示更好的 UI,但我有兴趣修复错误,而不是包含它。

随着更新状态的行被注释掉,回调工作并没有问题地记录到控制台,而如果我在回调中更新状态(所以我可以驱动 UI 更新),它会崩溃。我尝试了许多解决方法并在钩子和组件之间转移责任,但最后我确实需要在组件中更新状态,这会导致崩溃。奇怪的是,完成流也会通过一个非常相似的回调来更新状态,但那是有效的。

我究竟做错了什么?它可以得到帮助或解决吗?

标签: javascriptreactjsrxjsreact-hooks

解决方案


在 RxJS 中,任何错误或完整调用基本上都会“杀死”一个流。在您setHasError(true)进入“示例”组件后,它会重新渲染并再次运行此行:

const [val, setVal] = useSubject(subject, onError, onComplete);

该钩子以:

const [value, setValue] = useState(subject.getValue());

错误主题上的 getValue() 会引发错误。


推荐阅读