David
David

Reputation: 341

setTimeout not working as I want? (React)

setTimeout(() => this.setState({ messageSent: true }), 2000);

In the state initially messageSent: false, and I just want this text to appear for 2 seconds. Instead, what it does is completely the opposite: it appears after 2 seconds (and forever). Why?

{this.state.messageSent ? (
    <span className="message-sent">
        Your message has been sent. We will get back to you soon. Thank you for contacting us!
    </span>
) : null}

Upvotes: 0

Views: 3283

Answers (6)

Johan Syah
Johan Syah

Reputation: 21

Your code won't work because the setState is being called after the timeOut.

Here is the logic that you after:

setState(messageSent) -> true
wait 2000ms
setState(messageSent) -> false

You can add a call back at the end of your setState as follow:

this.setState({ messageSent: true }, () => { 
     setTimeout(() => this.setState({ messageSent: false }), 2000);
});

Has been answered by: https://stackoverflow.com/a/61658196/5653540

I just added a little bit of explanation. (new users can't add an extra comment) =(

Upvotes: 1

xdeepakv
xdeepakv

Reputation: 8125

You need to work with state, Like loading for first time would be null. On submit, it will be true/loading. Once loaded, need to be false.

Or you can create a component like Toast and based on data you can play.

function Toast({open, message, autoClose, type = "info", timeout = 2000}) {
  const [status, setStatus] = useState(open);
  useEffect(() => {
    if(autoClose) {
      setTimeout(() => {
      setStatus(false);
    }, timeout);
    }
  }, []);
  return (
    <div>
      {status  ? (
        <span className={"message-sent" + " " +type}>
          {message}
        </span>
      ) : null}
    </div>
  )
}

See below example:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>Hello World</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  <style>
    .error {
      background-color: red;
    }
    .info {
      background-color: blue;
    }
  </style>
</head>

<body>
  <div id="root">Test</div>
  <script type="text/babel">
    const { createElement, useState, useEffect, useRef } = React;
function Toast({open, message, autoClose, type = "info", timeout = 2000}) {
  const [status, setStatus] = useState(open);
  useEffect(() => {
    if(autoClose) {
      setTimeout(() => {
      setStatus(false);
    }, timeout);
    }
  }, []);
  return (
    <div>
      {status  ? (
        <span className={"message-sent" + " " +type}>
          {message}
        </span>
      ) : null}
    </div>
  )
}
function Loader() {
  const [status, setStatus] = useState(null);
  useEffect(() => {
    setTimeout(() => {
      setStatus(false);
    }, 2000);
  }, [status]);
  const onSubmit = () => {
    setStatus(true);
  };
  return (
    <div>
      {status === true ? (
        <span className="message-sent">
          Your message has been sent. We will get back to you soon. Thank you
          for contacting us!
        </span>
      ) : null}
      <button onClick={onSubmit}>Click here to see message</button>
    </div>
  );
}
ReactDOM.render(<div>
  <Loader />
  <Toast open message={"Auto close message"} autoClose type="error" />
  <Toast open message={"info message"} autoClose timeout={4000}/>
  </div>, document.getElementById("root"));

</script>
</body>

</html>

Upvotes: 0

Ron
Ron

Reputation: 6735

The below is easiest solution by using &&

!this.state.showSentMessage && (
    <span className="message-sent">
        Your message has been sent. We will get back to you soon. Thank you for contacting us!
    </span>
)

Upvotes: 0

Narendra Chouhan
Narendra Chouhan

Reputation: 2319

Try this:

this.setState({ showSentMessage: true }, () => {
    setTimeout(() => this.setState({ showSentMessage: false }), 2000);
})

rendeer Code

{
    this.state.showSentMessage ? (
        <span className="message-sent">
            Your message has been sent. We will get back to you soon. Thank you for contacting us!
    </span>
    ) : null
}

Upvotes: 2

croatia_91
croatia_91

Reputation: 1

Reverse it and it should appear immediately and then hide after 2 seconds because of the timeout

!this.state.messageSent ? (
    <span className="message-sent">
        Your message has been sent. We will get back to you soon. Thank you for contacting us!
    </span>
) : null}

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 370689

You need to invert the logic - currently, since you're setting messageSent to true after 2000ms, this.state.messageSent ? ( jsx ) : null enters the jsx branch after 2000ms.

It'll probably be clearer if you change the property to something like showSentMessage, which starts out true, then set it to false after 2000ms:

setTimeout(() => this.setState({ showSentMessage: false }), 2000);
{this.state.showSentMessage? (
    <span className="message-sent">
        Your message has been sent. We will get back to you soon. Thank you for contacting us!
    </span>
) : null}

Upvotes: 0

Related Questions