2

Got a nagging issue and was wondering if anyone can shed some light.

I made a function that automates the routing for my react app...but i am trying to attach a button to this function to ensure it starts and stops on button click. However, when i try the code below...nothing happens

class App extends React.Component {
  constructor (props) {
  super(props);
  this.state = { tabControl: true };
  this.handleClick = this.handleClick.bind(this);
  this.tabControl = this.tabControl.bind(this);
}

tabControl(props){
    RoutePaths(this.props);
}
handleClick() {
    this.setState(function (prevState, props){
      return { tabControl: !prevState.tabControl }
  });

}

render() {

return (
    <div className="clearfix" id="topContent">
          <Sidebar />
          <div className="white-bg" id="page-wrapper">
              <Header tagline="Welcome to JuDGE" />

             <button className="AutoTab" onClick={this.handleClick}>
                Toggle
              </button>

........

but when i try the second code, the tabbing function starts onClick of the button but of course doesn't stop when you click the button again.

 class App extends React.Component {

 constructor (props) {
  super(props);
  this.state = { tabControl: true };
  this.handleClick = this.handleClick.bind(this);
  this.tabControl = this.tabControl.bind(this);
}

 tabControl(props){
    RoutePaths(this.props);
 }


  handleClick() {
    this.setState(function (prevState, props){
      return { tabControl: !prevState.tabControl }
  });

 }

 render() {

 return (
    <div className="clearfix" id="topContent">
          <Sidebar />
          <div className="white-bg" id="page-wrapper">
              <Header tagline="Welcome to JuDGE" />

             <button className="AutoTab" onClick={this.tabControl}>
                Toggle
              </button>
6
  • What does the RoutePaths definition look like? Commented Nov 12, 2017 at 21:30
  • Here you go... function RoutePaths(props){ let pathUrls = ['/deploymentqueue', '/deploydb', '/currentstatus']; let paths = pathUrls.length; let index = 0; let interval = 3000; setInterval(() => { props.history.push(pathUrls[index]); index = (index + 1) % paths; }, interval); } module.exports = RoutePaths; Commented Nov 12, 2017 at 22:06
  • is App connected or rendered via the router? do you get any errors? Commented Nov 12, 2017 at 22:36
  • @Sag1v its rendered via the router Commented Nov 12, 2017 at 22:54
  • yeah you got other issue here, see my answer for more details Commented Nov 12, 2017 at 22:57

3 Answers 3

1

Try using the current state instead of the optional callback inside setState:

handleClick() {
  this.setState({ tabControl: !this.state.tabControl });
}
Sign up to request clarification or add additional context in comments.

Comments

1

I'm not sure i fully get what you are trying to do but it seems to me that you forgot a condition.
You say if you invoke this method:

tabControl(props){
    RoutePaths(this.props);
 }

it works but won't stop.

Well, you are not running it conditionally.
In this method:

handleClick() {
    this.setState(function (prevState, props){
      return { tabControl: !prevState.tabControl }
  });

}

You are setting the tabControl state. I think you forgot to check it before running tabControl().

tabControl(props){
    const {tabControl} = this.state;
    tabControl && RoutePaths(this.props); // invoke of tabControl is true
 }

Edit

After seeing the code for RoutePaths as you posted on comments:

function RoutePaths(props) {
    let pathUrls = ['/deploymentqueue', '/deploydb', '/currentstatus'];
    let paths = pathUrls.length;
    let index = 0;
    let interval = 3000;
    setInterval(() => {
        props.history.push(pathUrls[index]);
        index = (index + 1) % paths;
    }, interval);
} 

It seems to me that you will have another problem. you need the id of the interval that returned from setInterval in order to stop it, but you didn't stored it anywhere.
Quote from the docs:

... It returns an interval ID which uniquely identifies the interval, so you can remove it later by calling clearInterval() ...

So you will need to store it somewhere and call clearInterval with ID.

this.intervalId = setInterval(() => {...});

And somewhere else in your class:

clearInterval(this.interval);

Edit #2

As a followup to your comment, here is a simple usage of interval with react:

class Timer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      ticks: 0
    };
  }

  onStart = () => {
    this.intervalId = setInterval(() => {
      this.setState({ ticks: this.state.ticks + 1 })
    }, 500);
  }

  onStop = () => {
    clearInterval(this.intervalId)
  }

  render() {
    const { ticks } = this.state;
    return (
      <div>
        <button onClick={this.onStart}>Start</button>
        <button onClick={this.onStop}>Stop</button>
        <div>{ticks}</div>
      </div>
    );
  }
}

ReactDOM.render(<Timer />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

So you can try this approach,
RoutePaths will return the interval id:

function RoutePaths(props) {
    let pathUrls = ['/deploymentqueue', '/deploydb', '/currentstatus'];
    let paths = pathUrls.length;
    let index = 0;
    let interval = 3000;
    return setInterval(() => {
        props.history.push(pathUrls[index]);
        index = (index + 1) % paths;
    }, interval);
}

and tabControl will store the id and conditionally call or clear the interval:

tabControl() {
    const { tabControl } = this.state;
    if (tabControl && this.intervalId) { // i'm not sure this is the condition you want, but you can play with it
        clearInterval(this.intervalId);
    } else {
        this.intervalId = RoutePaths(this.props);
    }
}

I haven't tested this code but i think it can lead you to a good start.

4 Comments

Thanks for your detailed answer. I changed the tabControl function as you have adjusted above. but that didnt work or give any errors. To clarify what i am trying achieve, from my original post, if i directly call the tabControl method within the button onClick....it starts that set interval function....but i am trying to start and stop it from the said button. and as for the setInterval problem you mentioned, can you kindly elaborate. Any help is highly appreciated.
i've updated my answer with a simple example of how to use intervals. you must store the id that returned from setInterval and use it later with clearInterval in order to stop it.
Thanks for your help. your code did make it start and stop. I have accepted your answer and thanks for your patience. also, i noticed the start and stop works once....and doesnt restart again once. in this instance, in introduce a while loop right?
It probably doesn't start again because this.intervalId will still be assigned to the ID of the interval that was cleared and the if statement is evaluating to true. Take another look at my answer
0

You don't need tabControl state for what you are trying to do. However, you need to call clearInterval somewhere. Change your handleClick to something like this:

handleClick() {
  // change RoutePath to return the id that setInterval returns.
  if (this.routePathInterval) {
    clearInterval(this.routePathInterval);
    this.routePathInterval = null;
  } else {
    this.routePathInterval = RoutePath(this.props);
  }
}

Also, when you call clearInterval and then start it again, your index will start over from zero. You may want to keep the current index in state and pass it to RoutePaths, if you want to resume from the index that you were on.

edit:

On second thought, you don't need to keep the index in state, since you don't want to trigger a re-render when you increment it. However, you should make index an instance variable and make RoutePath an instance method of your App component.

First, initialize this.index = 0; in your constructor and then:

routePaths() {
    let pathUrls = ['/deploymentqueue', '/deploydb', '/currentstatus'];
    let paths = pathUrls.length;
    let interval = 3000;
    return setInterval(() => {
        this.props.history.push(pathUrls[index]);
        this.index = (this.index + 1) % paths;
    }, interval);
}

5 Comments

Thanks @Max. Your answer was equally as good. Appreciate your patience and help.
Hi @Max, after carefully looking at your answer, it solves my original question but also provides an idea of maintaining state for the interval. Thank you!
Hi @Max...any chance sharing an example of the current index suggestion for the clearinterval?
Updated my original answer.
To Solve the concern raised by @Max, I changed my index = 0 to index = pathUrls.indexOf(this.props.location.pathname) This allows react router keep track of where my route is at all times. Cheers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.