how to trigger an event after component did mount with react hooks

Viewed 2733

When a user clicks a button, a component is supposed to be mounted, once the component is mounted, window.print() is supposed to be run and then the component is supposed to be unmounted again.

With component lifecycles this was easy but with hooks I am not sure how to solve this.

export default function App() {
  const [showCmp, setShowCmp] = useState(false);

  const handleClick = () => {
    setShowCmp(true);

    window.print();

    setShowCmp(false);
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      {showCmp && <Cmp1 />}
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

I tried using useEffect but it didn't work:

useEffect(() => {
        if (showCmp) window.print();
}, [showCmp]);

here is a sandbox

how do I determine when the component is mounted correctly?

2 Answers

If you are simply wanting to mount the child component, print the screen, and then unmount it you can do all this in a useEffect hook.

Issues

Basically you can't enqueue cancelling state updates within a single render cycle, i.e.

const handleClick = () => {
  setShowCmp(true);
  window.print();
  setShowCmp(false);
};

Would enqueue the true state update, print the screen (without the child mounted), and enqueue the false state update, the net effect being state isn't really updated and you printed the screen without the child.

Solution

By splitting up the state updates you can fit a print screen between them.

useEffect(() => {
  if (showCmp) { // <-- (2a) if true, then...
    window.print(); // <-- (3) print screen
    setShowCmp(false); // <-- (4) toggle back false
  }
}, [showCmp]);

const handleClick = () => {
  setShowCmp(true); // <-- (1) toggle true to trigger effect
};

...

{showCmp && <Cmp1 />} // <-- (2b) if true, mount and render component

Child component

If you simply want to know when a component has mounted, use an useEffect with empty dependency array.

const Cmp1 = () => {
  React.useEffect(() => {
    console.log('Mounted!!');
  }, [])
  return <div>This is my Component</div>;
};

Demo

Edit how-to-trigger-an-event-after-component-did-mount-with-react-hooks

Hint: Be sure to also provide the code for <Cmp1 /> :)

Analysis

Looking at your Code Sandbox, you have:

Cmp1.js

import React from "react";

const Cmp1 = () => {
  return (
    <div>
      This is my Component{" "}
      <img src="https://upload.wikimedia.org/wikipedia/commons/4/43/Very_Large_Array%2C_2012.jpg" />
    </div>
  );
};

export default Cmp1;

Based on this, it seems like your question is not "how can I trigger an event after a component mounts?" but instead "how can I trigger an event after a resource loads?"

This is an important distinction because the time at which React finishes rendering does not equal the time at which an image has finished loading.

You can use the onLoad handler for <img> which is invoked when the image has finished loading (see MDN docs for more info on the onload event handler).

Solution

  1. Modify Cmp1 to accept an onLoad prop:

    import React from "react";
    
    const Cmp1 = (props) => {
      return (
        <div>
          This is my Component{" "}
          <img
            src="https://upload.wikimedia.org/wikipedia/commons/4/43/Very_Large_Array%2C_2012.jpg"
            alt="The 'Very Large Array' observatory"
            onLoad={props.onLoad}
          />
        </div>
      );
    };
    
    export default Cmp1;
    
  2. Create an onLoad handler in the parent component which calls window.print() and pass it down to <Cmp1>:

    import React, { useState } from "react";
    
    import Cmp1 from "./Cmp1";
    
    import "./styles.css";
    
    export default function App() {
      const [showCmp, setShowCmp] = useState(false);
    
      const handleClick = () => {
        setShowCmp(true);
      };
    
      const onLoad = () => {
        // Wait until the image loads to print
        window.print();
    
        // Hide the component after printing
        setShowCmp(false);
      };
    
      return (
        <div className="App">
          <h1>Hello CodeSandbox</h1>
          <h2>Start editing to see some magic happen!</h2>
    
          {/* Pass the `onLoad` callback down to the child */}
          {showCmp && <Cmp1 onLoad={onLoad} />}
          <button onClick={handleClick}>Click me</button>
        </div>
      );
    }
    
  3. Now, the browser will not call print() until the image is loaded.

    Example of printing after image loads

Edit epic-jennings-kj7g2

Other Notes

In the event that your image fails to load, you can use the onError React event handler in the <img> component. See the MDN docs for more info on the onerror event handler.

Related