简体   繁体   English

反应:番茄钟:考虑到状态的异步更新,如何正确更新状态?

[英]React: Pomodoro Clock: How to properly update state considering state's asynchronous updates?

In this React Pomodoro Clock, there is a function called sessionIncrement() that attempts to update a value in state to seconds, and then immediately update it again with a conversion.在这个 React Pomodoro Clock 中,有一个名为sessionIncrement()的函数,它尝试将状态值更新为秒,然后立即通过转换再次更新它。 However, the conversion does not have any effect, and it appears that this is because of the asynchronous nature of state.但是,转换没有任何效果,这似乎是因为状态的异步性质。 Any help would be greatly appreciated.任何帮助将不胜感激。

index.js:索引.js:

import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';

/*
* A simple React component
*/
const initState = {
  breakLength: 5,
  breakSeconds: undefined,
  sessionLength: 25,
  sessionSeconds: undefined,
  init: 'session',
  timeLeft: undefined,
  started: false,
  intervalFunc: undefined
}

class Clock extends React.Component {

  constructor(props) {
    super(props);
    this.state = initState;
    this.breakDecrement = this.breakDecrement.bind(this);
    this.breakIncrement = this.breakIncrement.bind(this);
    this.sessionDecrement = this.sessionDecrement.bind(this);
    this.sessionIncrement = this.sessionIncrement.bind(this);
    this.startStop = this.startStop.bind(this);
    this.reset = this.reset.bind(this);
  }

  componentDidMount() {
    // seconds are used for the countDown()
    // seconds are converted to MM:SS at every t-minus
    let breakSeconds = this.state.breakLength * 60;
    let sessionSeconds = this.state.sessionLength * 60;

    let time = sessionSeconds;

    // Convert to MM:SS
    const secondsToMins = (time) => {
      let converted = Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2);
      return converted;
    }

    // Initialize everything
    this.setState({ breakSeconds: breakSeconds }, () => console.log(this.state.breakSeconds));
    this.setState({ sessionSeconds: sessionSeconds }, () => console.log(this.state.sessionSeconds));
    this.setState({ timeLeft: secondsToMins(time) }, () => console.log(this.state.timeLeft));
  }

  breakDecrement() {
    // decrements the breakLength and the breakSeconds
    // breakLength is only a number ie. 5 (does not show seconds)
    // breakSeconds is that nunber converted into seconds
    const toSeconds = () => {
      let breakSeconds = this.state.breakLength * 60;
      this.setState({ breakSeconds: breakSeconds }, () => console.log(this.state.breakSeconds));
    }

    let breakLength = this.state.breakLength - 1;
    this.setState({ breakLength: breakLength }, toSeconds );

  }

  breakIncrement() {
    // same as decrement except does increment
    const toSeconds = () => {
      let breakSeconds = this.state.breakLength * 60;
      this.setState({ breakSeconds: breakSeconds }, () => console.log(this.state.breakSeconds));
    }

    let breakLength = this.state.breakLength + 1;
    this.setState({ breakLength: breakLength }, toSeconds);

  }

  sessionDecrement() {
    // decrements the sessionLength and the sessionSeconds
    // sessionLength is only a number ie. 25 (does not show seconds)
    // sessionSeconds is that nunber converted into seconds
    const toSeconds = () => {
      let sessionSeconds = this.state.sessionLength * 60;
      this.setState({ sessionSeconds: sessionSeconds }, () => console.log(this.state.sessionSeconds));
      this.setState({timeLeft: sessionSeconds});
    }

    // Convert to MM:SS
    const secondsToMins = (time) => {
      let converted = Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2);
      return converted;
    }

    let sessionLength = this.state.sessionLength - 1;
    let time = this.state.sessionSeconds;
    this.setState({ sessionLength: sessionLength }, toSeconds);
    this.setState({ timeLeft: secondsToMins(time) }, () => console.log(this.state.timeLeft));

  }

  sessionIncrement() {
    // same as decrement except does increment
    const toSeconds = () => {
      let sessionSeconds = this.state.sessionLength * 60;
      this.setState({ sessionSeconds: sessionSeconds }, () => console.log(this.state.sessionSeconds));
      this.setState({ timeLeft: sessionSeconds});
    }

    // Convert to MM:SS
    const secondsToMins = (time) => {
      let converted = Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2);
      return converted;
    }

    let time = this.state.sessionSeconds;
    let sessionLength = this.state.sessionLength + 1;
    this.setState({ sessionLength: sessionLength }, toSeconds);
    this.setState({ timeLeft: secondsToMins(time) }, () => console.log(this.state.timeLeft));

  }

  startStop(id) {
    // starts the countDown, which runs continuously until the start/stop button
    // is pressed again, which pauses the countdown.
    // the id parameter is used by countDown to play the audio beep
    if(!this.state.started){
      this.countDown(id);
      this.setState({ started: true});
    }
    // pauses the countDown
    if(this.state.started){
      let intervalFunc = this.state.intervalFunc;
      clearInterval(intervalFunc);
      this.setState({ started: false});
    }
  }

  reset() {
    // reset state to default values
    this.setState({ breakLength: 5 });
    this.setState({ sessionLength: 25 });
    this.setState({ init: 'session' });
    this.setState({ timeLeft: '25:00' });
  }

  countDown(id){

    // set the function to a variable and set state to it, so the function
    // can be paused with clearInterval()
    var intervalFunc = setInterval(() => down(this.state.sessionSeconds--), 1000);
    this.setState({intervalFunc: intervalFunc});


    // seconds are converted to MM:SS at every t-minus
    const down = (tMinus) =>
    {
      console.log(tMinus)
      let time = tMinus;
      const secondsToMins = (time) => {
        let converted = Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2);
        return converted;
      }

      // converts seconds to MM:SS at every t-minus
      this.setState({ timeLeft: secondsToMins(time) }, () => console.log(this.state.timeLeft));

      // when sessionSeconds reaches 0, start the break
      if(this.state.sessionSeconds == 0 && this.state.init == 'session'){
        let sound = document.getElementById(id).childNodes[0];
        sound.play();
        let breakSeconds = this.state.breakSeconds;
        this.setState({ init: 'break' });
        this.setState({ timeLeft: secondsToMins(breakSeconds) });
      }

      // when breakSeconds reaches 0, start the session
      if(this.state.timeLeft == 0 && this.state.init == 'break'){
        let sound = document.getElementById(id).childNodes[0];
        sound.play();
        let sessionSeconds = this.state.sessionSeconds;
        this.setState({ init: 'session' });
        this.setState({ timeLeft: secondsToMins(sessionSeconds) });
      }
    }
  }


  render() {
    return (
      <div id="clock">
      <h1 id="title">25-5 Clock</h1>

      <div>
      <p id="break-label">Break Length</p>
      <p id="break-length">{this.state.breakLength}</p>
      <button id="break-decrement" onClick={e => this.breakDecrement()}> Decrease </button>
      <button id="break-increment" onClick={e => this.breakIncrement()}> Increase </button>
      </div>

      <div>
      <p id="session-label">Session Length</p>
      <p id="session-length">{this.state.sessionLength}</p>
      <button id="session-decrement" onClick={e => this.sessionDecrement()}> Decrease </button>
      <button id="session-increment" onClick={e => this.sessionIncrement()}> Increase </button>
      </div>

      <hr/>

      <div>
      <p id="timer-label">{this.state.init}</p>
      <p id="time-left">{this.state.timeLeft}</p>
      <button id="start_stop" onClick={e => this.startStop(e.target.id)}><audio id="beep" src='./beep.mp3'></audio> start/stop </button>
      <button id="reset" onClick={e => this.reset()}> reset </button>
      </div>

      </div>
    );
  }
};

/*
* Render the above component into the div#app
*/
ReactDOM.render(<Clock />, document.getElementById("app"));

index.html索引.html

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <title>25-5 Clock</title>
  <style>
  </style>
</head>
<body>
  <main>
    <div id="app"></app>
    </main>
  <script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
  </body>
  </html>

Usually we try to avoid multiple state calls by updating the state in one setState function.通常我们通过在一个 setState 函数中更新状态来尝试避免多次状态调用。 In my experience I have met issues with updating my states with the current state.根据我的经验,我遇到了用当前状态更新我的状态的问题。 (ie, don't use this.state, try to use prevState) (即,不要使用 this.state,尝试使用 prevState)

/* We could use code like the following to update specific properties */
this.setState({ key1: newValue1, key3: newValue3 });

your code你的代码

sessionIncrement() {
// same as decrement except does increment
    const toSeconds = () => {
        let sessionSeconds = this.state.sessionLength * 60;
        this.setState({ sessionSeconds: sessionSeconds }, () => console.log(this.state.sessionSeconds));
        this.setState({ timeLeft: sessionSeconds});
    }

// Convert to MM:SS
    const secondsToMins = (time) => {
        let converted = Math.floor(time / 60) + ':' + ('0' + Math.floor(time % 60)).slice(-2);
        return converted;
    }

    let time = this.state.sessionSeconds;
    let sessionLength = this.state.sessionLength + 1;
    this.setState({ sessionLength: sessionLength }, toSeconds);
    this.setState({ timeLeft: secondsToMins(time) }, () => console.log(this.state.timeLeft));
}

Not sure why you are updating timeLeft twice so I chose to update timeLeft with the last value that you updated it with.不知道你为什么要更新 timeLeft 两次,所以我选择用你更新它的最后一个值来更新 timeLeft。

using one setState:使用一个 setState:

this.setState(prevState => ({
    sessionLength: prevState.sessionLength+1,
    sessionSeconds: (prevState.sessionLength+1)*60,
    timeLeft:  secondsToMins((prevState.sessionLength+1)*60)}), callbackFunction
);

For more information on how to use states see the documentation .有关如何使用状态的更多信息,请参阅文档

Hopefully nesting all your state updates into one setState will solve your problems with setState and it's asynchronous nature.希望将所有状态更新嵌套到一个 setState 中可以解决 setState 的问题,而且它是异步的。

You can also add more callbacks.您还可以添加更多回调。

this.setState({}, func1, func2, func3);
or 
this.setState({}, () => {func1, func2, func3});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM