colin_dev256
colin_dev256

Reputation: 815

How to wrap a handleClick around reactjs calls

I'm trying to build a horse race app and being new to react, I'm struggling with handling a click event when a user clicks the 'Start' button for the jockey to race. When I remove the handleClick function (as commented out below), the jockey races (using react's progress bar) automatically when I reload the page. I only want that functionality when I click the 'Start' button.

App.js

import React, { Component } from 'react';
import Progress from 'react-progressbar';
import logo from '../../images/logo.svg';
import './App.css';


class Jockey extends Component {
  state = {
    interval: Math.floor(Math.random() * 500),    
    progress: 5,
  }

  // handleClick = () => {
    // console.log('The link was clicked.');
    componentDidMount = () => {
      this.interval = setInterval(this.timer, this.state.interval);
    }

    timer = () => {
      this.setState({ progress: this.state.progress + 1 });
      // (this.state.progress >= 99) ? this.setState({ progress: 100 }) : "" ;
      if(this.state.progress >= 99) {
         this.setState({ progress: 100 }) 
      };
    }

  // } 

  render() {
    const Buttons = () => (
      <div className="App-buttons">
        <button className="ui primary button" onClick={this.handleClick}>Start</button>
        <button className="ui button">Reset</button>
      </div>
    );

    return (
      <div>
        <Buttons />
        <div className="App-field">
          <h1>Race Track</h1>
          <img src="https://avatars1.githubusercontent.com/u/3757315?v=4" alt=""/>
          <Progress className="App-progress" completed={this.state.progress}/>
          
        </div>
      </div>
    );
  }

}

export class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to the React Race Hustle</h1>
        </header>
        <Jockey />
      </div>
      
    );
  }
}

Upvotes: 1

Views: 355

Answers (2)

Brett DeWoody
Brett DeWoody

Reputation: 62773

With your code as is (with the handleClick commented out), the componentDidMount() function will automatically run when the component mounts. Which means the timer() will begin running on the defined interval when the component mounts.

You'll want to make two changes:

  1. Move the timer() into the handler
  2. Move the componentDidMount() function outside the handler

handleClick = () => {
  console.log('The link was clicked.');
  this.interval = setInterval(this.timer, this.state.interval);

  timer = () => {
    this.setState((prevState) => ({ progress: (prevState.progress + 1 > 98 ? 100 : prevState.progress + 1) });
  }
}

And we no longer need the componentDidMount() function.

Upvotes: 3

Giorgi Moniava
Giorgi Moniava

Reputation: 28654

I tried to change your code so that it satisfied your needs; hope you can see kind of changes it needed.

import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './Hello';


class Jockey extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      interval: Math.floor(Math.random() * 500),
      progress: 5,
    }
    this.handleClick=this.handleClick.bind(this);
  }

  handleClick (){
    let that = this;
    this.interval = setInterval(function () {
      that.setState((ps)=>{ 
        if(ps.progress + 1>=99) return {progress:100}
        return {progress: ps.progress + 1 }}
        );

    }, this.state.interval);
  }



render() {
  const Buttons = () => (
    <div className="App-buttons">
      <button className="ui primary button" onClick={this.handleClick}>Start</button>
      <button className="ui button">Reset</button>
    </div>
  );

  return (
    <div>
      <Buttons />
      <div className="App-field">
        <h1>Race Track</h1>
        <img src="https://avatars1.githubusercontent.com/u/3757315?v=4" alt="" />
        <div className="App-progress" completed>{ this.state.progress }</div>

    </div>
      </div >
    );
}

}

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

Some comments:

  • I used functional setState too; because what you had is not the right way to set state when state depends on value from previous state.

  • You can also call clearInterval when component unmounts.

  • And I have rendered the progress inside a div instead of a Progress component.

  • and I would not put interval inside state; you normally don't put stuff in state if it is not used inside render.

Upvotes: 2

Related Questions