Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to make a child React component show for a certain amount of time?

I have a React component called PopUpBanner that I use to show messages. For example, in my login component, I use it like this. If an error occurs, then I set the bannerMessage state to have text so that the banner shows:

        this.setState({
          bannerMessage: {
            msg: error.message + ". Incorrect email address or password.",
            isError: true,
          },
        });

Here is how the component is then used:

<PopUpBanner
    message={bannerMessage.msg}
    isError={bannerMessage.isError}
></PopUpBanner>

And here is the PopUpBanner class:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import React, { Component } from "react";

class PopUpBanner extends Component {
  constructor(props) {
    super(props);
    this.state = {
      message: this.props.message,
    };
  }

  // TODO : not in use
  reset = () => {
    this.resetId = setTimeout(
      function () {
        this.setState({ message: "" });
      }.bind(this),
      3000
    );
  };

  componentDidMount() {}

  componentWillUnmount() {
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
      console.log("clearing time out");
    }
  }

  render() {
    const message = this.props.message;
    const isError = this.props.isError;
    return (
      <div style={message != "" ? { display: "block" } : { display: "none" }}>
        <div>
          {isError ? (
            <div
              className="alert alert-danger text-center"
              role="alert"
              style={{ width: "50%", margin: "auto" }}
            >
              {message}
            </div>
          ) : (
            <div
              className="alert alert-primary text-center"
              role="alert"
              style={{ width: "50%", margin: "auto" }}
            >
              {message}
            </div>
          )}
        </div>
      </div>
    );
  }
}

export default PopUpBanner;

The problem is that the PopUpBanner is shown until the page is refreshed or navigated to another page.

So if you look at the PopUpBanner class, I attempted to use setTimeout but wasn’t able to finish it.

Any ideas on how I can transform PopUpBanner component to be on a timer?

>Solution :

I see two options:

  1. Handle it in the parent component, only rendering PopUpBanner when it should be there, using setTimeout to trigger a state update that re-renders the parent without rendering PopUpBanner.

  2. Handle it in PopUpBanner, returning null from render after the expiration.

I would prefer #1 over #2. But your existing code is basically doing #2, you just have to adjust render to support it:

render() {
    const message = this.props.message;
    if (!message) {
        return null;
    }
    // ...show the message...

But as discussed in teh comments, I wouldn’t copy props to state like that. So instead:

constructor(props) {
    super(props);
    this.state = {
        expiredMessage: null,
    };
}

then to expire a message:

setupExpiration() {
    this.expirationTimer = setTimeout(() => {
        this.setState(() => ({expiredMessage: this.props.message}));
    }, 1000); // <== Or however long you want it showing
}

…which you call from a couple of lifecycle methods:

componentDidMount() {
    this.setupExpiration();
}
componentDidUpdate() {
    this.setupExpiration();
}

and render becomes:

render() {
    const { expiredMessage } = this.state;
    const { message } = this.props;
    if (expiredMessage === message) {
        return null;
    }
    // ...show the message...

But again, I’d go for having the parent in control of this, actually removing PopUpBanner when it shouldn’t be showing:

class PopUpBanner extends React.Component {
    render() {
        const {message} = this.props;
        return <div class="banner">{message}</div>;
    }
}

class Parent extends React.Component {
    state = {
        message: null,
    };

    constructor(props) {
        super(props);
        this.showMessage = this.showMessage.bind(this);
        this.messageTimer = 0;
    }

    showMessage() {
        clearTimeout(this.messageTimer);
        this.setState({message: "Hi there, I'm a banner"});
        this.messageTimer = setTimeout(() => {
            this.setState({message: null});
        }, 1000);
    }

    render() {
        const {message} = this.state;
        const {showMessage} = this;
        return <div className="with-banner">
            {message && <PopUpBanner message={message} />}
            <div>
                <input type="button" value="Show Message" onClick={showMessage} />
            </div>
        </div>;
    }
};

ReactDOM.render(<Parent />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading