React – How to fix very inconsistent disabled button countdown (i.e., several whole seconds)



I’m using setTimeout() (I even tried driftless too) to change buttons from disabled = true to disabled = false 3 seconds after appearing on the DOM (onLoad=). The buttons appear in sequence, after the previous button has been clicked.

The problem is, the setTimeout() goes absolutely haywire: Each button’s disabled = true duration is seemingly sporadic (at least on Windows Chrome on 2 different PC’s). Sometimes the button seemingly waits the full 3 seconds to enable, sometimes it enables instantaneously, and everything in-between.

What is causing this inconsistency, and how can I get each button to enable exactly 3 seconds (well, within a couple hundred milliseconds…not a big deal) after appearing?

CodePen

Javascript

import React from 'react'
import ReactDOM from 'react-dom'

export default class MyForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = { is_button_disabled: true, show_button_1: true, show_button_2: false, show_button_3: false, show_button_4: false };
  }
  handleDisableButton = (event) => {
    setTimeout(() => this.setState({ is_button_disabled: false }), 3000);
  }
  handleShowButton = (buttonId) => {
    if (buttonId === 'button_1'){
      this.setState({ is_button_disabled: true})
      this.setState({ show_button_1: false, show_button_2: true})
    }
    if (buttonId === 'button_2'){
      this.setState({ is_button_disabled: true})
      this.setState({ show_button_2: false, show_button_3: true})
    }
    if (buttonId === 'button_3'){
      this.setState({ is_button_disabled: true})
      this.setState({ show_button_3: false, show_button_4: true})
    }
    if (buttonId === 'button_4'){
      this.setState({ is_button_disabled: true})
      this.setState({ show_button_4: false, show_button_1: true})
    }
  }
  render() {
    const { show_button_1, show_button_2, show_button_3, show_button_4 } = this.state;
    return (
      <div butttons>
        {show_button_1 &&
          <button onLoad={this.handleDisableButton()} onClick={() => this.handleShowButton('button_1')} disabled={this.state.is_button_disabled}>Button1</button>
        }
        {show_button_2 &&
          <button onLoad={this.handleDisableButton()} onClick={() => this.handleShowButton('button_2')} disabled={this.state.is_button_disabled}>Button2</button>
        }
        {show_button_3 &&
          <button onLoad={this.handleDisableButton()} onClick={() => this.handleShowButton('button_3')} disabled={this.state.is_button_disabled}>Button3</button>
        }
        {show_button_4 &&
          <button onLoad={this.handleDisableButton()} onClick={() => this.handleShowButton('button_4')} disabled={this.state.is_button_disabled}>Button4</button>
        }
      </div>
    )
  }
}

ReactDOM.render(<MyForm />, document.getElementById('root'));

HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <title>React App</title>
</head>

<body>
    <noscript>
        You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
</body>

</html>

Answer

You should fix this first.

<button id="myButton" onLoad={this.handleDisableButton} onClick={() => this.handleShowButton('button_1')} disabled={this.state.is_button_disabled}>Button1</button>

which means

this.handleDisableButton()

to

this.handleDisableButton

And clearTimeout to prevent memory leak

I have fixed your sandboxcode:

  componentDidMount() {
    this.timeoutHandler = setTimeout(() => {
      clearTimeout(this.timeoutHandler);
      this.setState({ is_button_disabled: false });
    }, 3000);
  }
  handleShowButton = (buttonId) => {
    let newState = {};
    if (buttonId === "button_1") {
      newState = {
        is_button_disabled: true,
        show_button_1: false,
        show_button_2: true
      };
    }
    if (buttonId === "button_2") {
      newState = {
        is_button_disabled: true,
        show_button_2: false,
        show_button_3: true
      };
    }
    if (buttonId === "button_3") {
      newState = {
        is_button_disabled: true,
        show_button_3: false,
        show_button_4: true
      };
    }
    if (buttonId === "button_4") {
      newState = {
        is_button_disabled: true,
        show_button_4: false,
        show_button_1: true
      };
    }
    this.setState(newState, () => {
      this.timeoutHandler = setTimeout(() => {
        clearTimeout(this.timeoutHandler);
        this.setState({ is_button_disabled: false });
      }, 3000);
    });
  };
  componentWillUnmount() {
    clearTimeout(this.timeoutHandler);
  }


Source: stackoverflow