React:卸载组件之后但安装另一个组件之前运行异步功能

时间:2020-07-25 16:43:16

标签: javascript reactjs react-lifecycle

希望有人可以帮助我朝正确的方向发展,或者指出可能有用的任何文档/学习。

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

在安装<Widget/>后,<Modal/>会使用条件渲染进行隐藏(反之亦然),并且在我的ShowModal状态值中使用相同的布尔值(通过按钮切换此布尔值)。我为此示例here

做了一个简化的CodeSandbox

我遇到麻烦的地方是我有一个异步功能takeScreenShot(),需要在完全卸载{strong> <Widget/>安装之前。调用<Modal/>时,<Widget/>(刚刚卸载)和<Modal/>(即将安装)在DOM中都不可见。我需要确保是这种情况,因为我正在截取基础页面的屏幕快照,在该屏幕上我不想在其中包括任何组件。在我的CodeSandbox示例中,屏幕截图功能的输出将呈现灰色背景,而不会显示蓝色或红色框

我尝试过的事情
我试过像这样在takeScreenShot()组件的useEffect钩子中运行清理功能。

<Widget/>

但是它不起作用,因为类似于 useEffect(() => { return () => takeScreenShot(); }, []); 的清理功能可以在组件即将卸载时运行,而不是完全从DOM上完全卸载。这使我的屏幕截图捕获了图像中的卸载组件。有人有想法将我指向正确的方向吗?

4 个答案:

答案 0 :(得分:2)

将代码从您的应用程序移动到Modal中,以便在Modal首次加载时被调用。我删除了对useEffectuseLayoutEffect的所有引用,尽管模式中仍可能需要useLayoutEffect

Code Sample Here ...

答案 1 :(得分:1)

您可以尝试在useLayoutEffect钩中运行它,就像...

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

请参阅... https://reactjs.org/docs/hooks-reference.html#uselayouteffect

根据文档...

签名与useEffect相同,但是它同步触发 在所有DOM突变之后。使用它从DOM中读取布局,并 同步重新渲染。在useLayoutEffect内部安排的更新将 在浏览器有机会绘画之前被同步刷新。

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

Code Sample Here ...

答案 2 :(得分:0)

您可能还有另一个状态布尔值,用于指示是否正在截屏,如果为true则不渲染这两个元素。

是这样的:

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

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

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

现在渲染时

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

答案 3 :(得分:0)

这是您可以(并且应该)这样做的方式。

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