React 18异步方式卸载root

jvl*_*lmr 11 reactjs react-dom react-18

我有一个罕见的用例,我需要在我的 React 组件中注册多个根,并在组件卸载时销毁它们。显然,我在渲染时卸载了根。root.unmount()我通过在 后立即调用来模拟这种情况root.render(...)。在以下示例中:https://codesandbox.io/s/eager-grothendieck-h49eoo? file=%2Fsrc%2FApp.tsx

这会导致以下警告: Warning: Attempted to synchronously unmount a root while React was already rendering. React cannot finish unmounting the root until the current render has completed, which may lead to a race condition.

这个警告对我来说意味着有一种异步方法可以卸载根目录,但我无法找到如何卸载根目录的方法。包装root.unmount()在异步函数 ( const unmount = async () => root.unmount()) 中不起作用。有任何想法吗?我在这里得到完全错误的东西吗?

kon*_*nqi 9

在我们的项目中,我们通过异步卸载setTimeout我创建了一个更新的codesandbox,您可以在这里找到工作示例。

下面的代码片段显示了如何处理安装和卸载。请注意,安装和卸载通过 与同步渲染循环分离setTimeout,这是为了避免同步安装和异步卸载之间的竞争。否则,可能会发生该组件在从先前的渲染中安装后立即被卸载的情况。

我不相信这是最好的解决方案,但到目前为止它对我们有效。

function MyComponent() {
  const containerRef = useRef<HTMLDivElement | null>(null);
  const rootRef = useRef<ReactDOM.Root>();

  useEffect(() => {
    const renderTimeout = setTimeout(() => {
      if (containerRef.current) {
        console.log("create root");
        rootRef.current =
          rootRef.current ?? ReactDOM.createRoot(containerRef.current);
      }

      if (containerRef.current && rootRef.current) {
        console.log("component render");
        rootRef.current.render(<div>mounted component</div>);
      }
    });

    return () => {
      clearTimeout(renderTimeout);
      console.log("unmount");
      const root = rootRef.current;
      rootRef.current = undefined;

      setTimeout(() => {
        console.log("component unmount");
        root?.unmount();
      });
    };
  }, [rootRef]);

  return <div ref={containerRef}></div>;
}
Run Code Online (Sandbox Code Playgroud)

  • 潜在的相关问题:https://github.com/facebook/react/issues/25675 (3认同)