简体   繁体   中英

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

hoping someone can help lead me in the right direction with this or point to me to any docs/learnings that might be helpful.

I have two React components:
<Widget/>
<Modal/>

When <Widget/> mounted, <Modal/> is hidden (and vice-versa) using conditional rendering and the same boolean in my ShowModal state value (this boolean gets switched by a button). I made a simplified CodeSandbox for this example here

Where I am getting stuck is I have an async function takeScreenShot() which needs to run after <Widget/> is unmounted completely and before <Modal/> is mounted. Neither <Widget/> (just unmounted) nor <Modal/> (about to mount) should be visible in the DOM when takeScreenShot() is called. I need to make sure this is the case because I am taking a screenshot of the underlying page where I do not want to include either of the components in this. In my CodeSandbox example the output of my screenshot function would render a the gray background without showing the blue or red box

What I have tried
I have tried running a cleanup function in the useEffect hook in the <Widget/> component like so.

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

However it doesn't work because the cleanup function, similar to componentWillUnmount() runs right as the component is about to unmount, not fully unmounted from the DOM. This causes my screenshot to capture the un-mounting component in the image. Does anyone have an ideas to point me in the right direction on this?

Move the code from your App into the Modal so that it gets called when the Modal Loads for the first time. I removed all references to useEffect or useLayoutEffect , although the useLayoutEffect may still be required in the modal.

Code Sample Here...

You could try running it in a useLayoutEffect hook like...

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

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

According to the Docs...

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.

You will probably need to keep a separate state of whether or not the modal has been loaded to ensure that the effect hook doesn't execute on initial load of your component.

Code Sample Here...

You could have another state bool that indicates if a screenshot is being taken, if thats true render neither of the two elements.

so something like this:

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

then within your takeScreenshot function, after you're done with its execution it sets it back to false

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

now while rendering

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

Here's how you can (and should) do it.

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>
  )
}

You can run the demo here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM