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

Time:03-11

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:

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?

CodePudding user response:

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 className="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>

  • Related