1

After a an AJAX request has returned a response, I want to give a feedback message to the user for 5 seconds - e.g. "Saved!".

class FlashMessage extends Component {
  state = { visible: false }

  updateVisibility = () => {
    this.setState({ visible: true }, () =>
      this.setTimeout(this.setState({ visible: false }), 5000)
    )
  }

  render() {
    this.updateVisibility()
    if (this.props.data && this.state.visible) {
      return <div>Saved!</div>
    }
    return false
  }
}

export default FlashMessage

Above is my futile attempt to create a component doing this, but obviously doesn't work because you:

Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state."

So my question is - how can one show a message for n seconds following a AJAX response.

Unfortunately I cannot rely on onClick here.

Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
  • Possible duplicate of [ReactJS: setTimeout() not working?](https://stackoverflow.com/questions/36270422/reactjs-settimeout-not-working) – Dan O Oct 07 '18 at 14:16
  • You should not call setTimeout from within setState, instead, call it after setState. – Ron F Oct 07 '18 at 14:18
  • Where is Ajax call in your code? Also the reason you get that warning because you are calling this.updateVisibility() function in render and that function actually doing setState and that’s why the warning. Your render part is completely wrong I would say – Hemadri Dasari Oct 07 '18 at 14:20
  • @Think-Twice The provided code is a child component that the parent pass the response object to. If you have any suggestion how to improve the code , I would be very grateful. – Fellow Stranger Oct 07 '18 at 14:22
  • @RonF But how can I even update? As described, `updateVisibility` cannot be run from within `render`. – Fellow Stranger Oct 07 '18 at 14:22
  • A Function which calls setState and you call it in render will cause such warnings – Hemadri Dasari Oct 07 '18 at 14:22
  • @Think-Twice Exactly. – Fellow Stranger Oct 07 '18 at 14:24

2 Answers2

2

You should do something like below. The below code does exactly what you want without any warnings and right way of doing things in your case.

class FlashMessage extends Component {
  state = { visible: false }

  componentWillMount(){
    this.setState({ visible: true });
  }

  componentDidMount(){
    this.timer = this.setTimeout(()=> {this.setState({ visible: false })}, 5000)
  }

  componentWillUnMount(){
    clearTimeout(this.timer);
  }

  render() {
    return(
        <div>
            {this.props.data && this.state.visible ? "Yes, data received!" : "Data not received"}
        </div>
    )
  }
}

export default FlashMessage;

Please not componentWillMount is deprecated in latest react versions but you can also use constructor if you don't want to use componentWillMount

Please let me know if you have any queries on the above code.

Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
Hemadri Dasari
  • 32,666
  • 37
  • 119
  • 162
0

This is really tricky concept because you cant change the state on render. I would highly recommend you to use a library to do it. In my case I use react-alert and it works great.

obiwankenoobi
  • 1,504
  • 5
  • 18
  • 37