React : 运行 异步函数在组件卸载之后但在另一个组件安装之前

React : Run async function after component unmounted but before another component mounts

希望有人可以帮助引导我朝着正确的方向前进,或者向我指出任何可能有帮助的 docs/learnings。

我有两个 React 组件:
<Widget/>
<Modal/>

<Widget/> 安装时,<Modal/> 被隐藏(反之亦然)使用条件渲染和我的 ShowModal 状态值中的相同布尔值(这个布尔值通过按钮切换).我为这个例子制作了一个简化的 CodeSandbox here

我卡住的地方是我有一个异步函数 takeScreenShot() 需要 运行 <Widget/> 卸载后 完全before <Modal/> 安装。当调用 takeScreenShot() 时,<Widget/>(刚刚卸载)和 <Modal/>(即将安装)都不应该在 DOM 中可见。我需要确保是这种情况,因为我正在截取底层页面的屏幕截图,我不想在其中包含任何一个组件。在我的 CodeSandbox 示例中,我的屏幕截图函数的输出将呈现灰色背景,而不显示蓝色或红色框

我试过的
我试过 运行 在 <Widget/> 组件的 useEffect 挂钩中使用清理函数,就像这样。

 useEffect(() => {
    return () => takeScreenShot();
  }, []);

但是它不起作用,因为清理功能类似于 componentWillUnmount() 运行s 在组件即将卸载时,而不是 完全 从 DOM 卸载。这导致我的屏幕截图捕获图像中的卸载组件。有没有人有想法为我指出正确的方向?

您可以在 useLayoutEffect 挂钩中尝试 运行 它,例如 ...

  useLayoutEffect(() => {
    if (!showModal) takeScreenshot();
  }, [showModal]);

看... https://reactjs.org/docs/hooks-reference.html#uselayouteffect

根据文档...

The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

您可能需要保持模式是否已加载的单独状态,以确保效果挂钩不会在组件的初始加载时执行。

Code Sample Here ...

您可以使用另一个状态布尔值来指示是否正在截取屏幕截图,如果是,则不渲染这两个元素中的任何一个。

所以像这样:

() => this.setState({screenshot: true}, () => this.takeScreenshot())

然后在您的 takeScreenshot 函数中,在您完成其执行后将其设置回 false

takeScreenshot = () => {
  // ...
  // ...
  this.setState({screenshot: false})
}

现在正在渲染

{!screenshot && (showModal ? <Modal /> : <Widget />)}

以下是您可以(并且应该)执行的方法。

async function takeScreenshot() {
  console.log("Taking screenshot...");

  return new Promise(resolve => {
    window.setTimeout(() => {
      resolve();
      console.log("Screenshot captured!");
    }, 3000);
  });
}

function Widget({ onUnmount }) {
  React.useEffect(() => {
    return () => onUnmount();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return <p>Widget</p>;
}

function Modal() {
  return <p>Modal</p>;
}

function App() {
  const [open, setOpen] = React.useState('widget');
  
  const handleUnmount = async () => {
    await takeScreenshot();
    setOpen('modal');
  }
 
  return (
    <React.Fragment>
      {open === 'widget' && <Widget onUnmount={handleUnmount} />}
      {open === 'modal' && <Modal />}

      <button
        onClick={() => {
          if (open !== "widget") return;
          setOpen("");
        }}
      >
        Show modal
      </button>
    </React.Fragment>
  )
}

您可以运行演示here

将代码从您的应用移至模态框,以便在模态框首次加载时调用它。我删除了所有对 useEffectuseLayoutEffect 的引用,尽管 useLayoutEffect 在模态中可能仍然需要。

Code Sample Here ...