简体   繁体   English

反应:状态不实时渲染

[英]React: State not rendering in real time

I am trying to build a web app that calculates how much money one should save per day for a future travel (I know it could easily be done by a simple calculator but this is for the sake of learning). 我正在尝试构建一个Web应用程序,该应用程序可以计算一个人每天为将来的旅行节省的钱(我知道可以用一个简单的计算器轻松地完成此操作,但这只是为了学习)。

My issue is that when I type a cost and time left in the two inputs the daily savings calculation does not render in real time on the DOM . 我的问题是,当我在两个输入中键入剩余的成本和时间时,每日节省的计算不会实时显示在DOM上

Here is what happens: 这是发生了什么:

  1. I type a cost in the first input (within Cost component) and the days left in the second input (within TimeLeft component) 我在第一个输入中(在Cost组件内)键入一个成本,在第二个输入中(在TimeLeft组件内)键入一个剩余的天数
  2. The DOM renders both the cost and the days left in real time as I type (in {this.state.totalCost} and {this.state.timeLeft} DOM在我输入时(在{this.state.totalCost}{this.state.timeLeft}中实时呈现成本和剩余天数。
  3. The calculations result {this.state.dailyCost} renders with a "one change" delay (ie Cost = 100, days left = 10. I need to type something else like adding a 0 in the cost for 10 to render in the DOM as a correct result of the calculation.) 计算结果{this.state.dailyCost} 呈现“一次更改”延迟 (即Cost = 100,剩余天数= 10)。 我需要输入其他内容,例如在成本中添加0以便10在DOM中呈现为计算的正确结果 。)

Thank you for your help! 谢谢您的帮助! I hope I explained in an understandable way. 我希望我以一种可以理解的方式进行解释。

My code: 我的代码:

    class App extends Component {
  constructor() {
    super();
    this.state = {
      totalCost: 0,
      timeLeft: 0,
      dailyCost: 0,
    }
  }

  updateDailySavings() {
    if (this.state.timeLeft !== 0) {
      this.setState({dailyCost: (this.state.totalCost / this.state.timeLeft).toFixed(2)});
    } else {
      this.setState({dailyCost: 0});
    }
  }

  changeCost(newCost) {
    this.setState({totalCost: Math.round(newCost)});
    this.updateDailySavings()
  }

  changeTimeLeft(newTimeLeft) {
    this.setState({timeLeft: Math.round(newTimeLeft)});
    this.updateDailySavings()
  }

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <Cost changeCost={this.changeCost.bind(this)}/>
        <TimeLeft changeTimeLeft={this.changeTimeLeft.bind(this)}/> <br/>
        <div>You have to save £{this.state.totalCost} in the next {this.state.timeLeft} days.<br/>
        You have to save £{this.state.dailyCost} per day.
        </div>
      </div>
    );
  }
}

Your onChange handler functions should be 您的onChange处理函数应为

changeCost(newCost) {
    this.setState({totalCost: Math.round(newCost)},
                  this.updateDailySavings);
  }

  changeTimeLeft(newTimeLeft) {
    this.setState({timeLeft: Math.round(newTimeLeft)},
                  this.updateDailySavings);
  }

From the docs , 文档中

setState() does not always immediately update the component. setState()并不总是立即更新组件。 It may batch or defer the update until later. 它可能会批量更新或将更新推迟到以后。 This makes reading this.state right after calling setState() a potential pitfall. 这使得在调用setState()之后立即读取this.state可能是一个陷阱。 Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. 而是使用componentDidUpdate或setState回调(setState(updater,callback)),确保在应用更新后均能触发这两种方法。

Since you want to use the updated state in your next setState call , you should use the callback. 由于要在下一个setState调用中使用更新后的状态,因此应使用回调。

EDIT: corrected the code after removing the extra () as pointed out by @bennygenel 编辑:删除@bennygenel指出的多余()后,更正了代码

Calculation does not render in real time on the DOM. 计算不会在DOM上实时呈现。

a) setState is asynchronous , subsequent calls in the same update cycle will overwrite previous updates, and the previous changes will be lost. a) setState异步的 ,在同一更新周期中的后续调用将覆盖先前的更新,并且先前的更改将丢失。

so if you want to use this.setState values you should use a callback function. 因此,如果要使用this.setState值,则应使用回调函数。

this.setState({value: 'value'},() => {return : /*something here ..*/});

Instead of mutating state ( this.state.someValue )directly its a good practice to return a fresh copy of state . 与其直接改变状态( this.state.someValue ), this.state.someValue直接返回状态的新副本。

return { ...previousState, value: updated new state };

You can read more Here 你可以在这里阅读更多

As pointed out by @bennygenel, setState is async, so you should do: 正如@bennygenel指出的那样,setState是异步的,因此您应该这样做:

updateDailySavings() {
    if (this.timeLeft !== 0) {
      this.setState({dailyCost: (this.totalCost / this.timeLeft).toFixed(2)});
    } else {
      this.setState({dailyCost: 0});
    }
  }

  changeCost(newCost) {
    this.totalCost = Math.round(newCost);
    this.setState({totalCost: this.totalCost});
    this.updateDailySavings()
  }

  changeTimeLeft(newTimeLeft) {
    this.timeLeft = Math.round(newTimeLeft);
    this.setState({timeLeft: this.timeLeft});
    this.updateDailySavings()
  }

*Note maybe the two later setStates are not needed anymore, but I don't know if you are using them after or not *请注意,也许以后不再需要两个setState了,但是我不知道您以后是否使用它们

for me I used async await and work pretty nice 对我来说,我使用了async await并且工作得很好

async updateDailySavings() {
    if (this.state.timeLeft !== 0) {
      await this.setState({dailyCost: (this.state.totalCost / this.state.timeLeft).toFixed(2)});
    } else {
      await this.setState({dailyCost: 0});
    }
  }

  async changeCost(newCost) {
    await this.setState({totalCost: Math.round(newCost)});
    this.updateDailySavings()
  }

  async changeTimeLeft(newTimeLeft) {
    await this.setState({timeLeft: Math.round(newTimeLeft)});
    this.updateDailySavings()
  }

I hope it can help you 希望对您有所帮助

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

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