反应:组件状态不更新记分员或计数器

Posted

技术标签:

【中文标题】反应:组件状态不更新记分员或计数器【英文标题】:React: Component state not updating scorekeeper or counter 【发布时间】:2018-05-28 11:50:55 【问题描述】:

每当我在四个选项中单击正确或错误的“答案”div 时,我都会尝试更新我的组件状态,以更新记分员和计数器。但是,当我控制台注销记分员和计数器值时,无论我回答了多少问题,我总是得到 0。为什么会发生这种情况,如何更新这两个组件状态?

这是我的代码:

export default class QuestionCard extends React.Component 
    constructor(props) 
        super(props);

        this.state = 
            scorekeeper: 0, 
            counter: 0,
            hideDiv: false
        

        this.handleClick = this.handleClick.bind(this);
        this.handleClickCont = this.handleClickCont.bind(this);
    

    handleClick(choice) 
        if(this.state.counter === 4) 
            return console.log('we have four')
        

        this.handleClickCont(choice)
    

    handleClickCont(choice) 
        if(choice === this.props.answer) 
            this.setState(scorekeeper: this.state.scorekeeper + 1);
            this.setState(counter: this.state.counter + 1);
            console.log('scorekeeper ' + this.state.scorekeeper)
            console.log('counter ' + this.state.counter)
            return this.setState(hideDiv: !this.state.hideDiv);
         

        this.setState(scorekeeper: this.state.scorekeeper - 1);
        this.setState(counter: this.state.counter + 1);
        console.log('scorekeeper ' + this.state.scorekeeper);
        console.log(this.state.counter)
        this.setState(hideDiv: !this.state.hideDiv);
    

    render() 
        return (
            <div style=margin: '20px', display: this.state.hideDiv ? 'none' : 'block'>
                <div>
                    this.props.question
                </div>

                <div style=
                        margin: '20px', 
                        width: '500px', 
                        display: 'flex', 
                        justifyContent: 'space-around',
                        display: this.state.hideDiv ? 'none' : 'block'
                      >
                    <div onClick=() => this.handleClick(this.props.choice1)>
                        this.props.choice1
                    </div>
                    <div onClick=() => this.handleClick(this.props.choice2)>
                        this.props.choice2
                    </div>
                    <div onClick=() => this.handleClick(this.props.choice3)>
                        this.props.choice3
                    </div>
                    <div onClick=() => this.handleClick(this.props.choice4)>
                        this.props.choice4
                    </div>
                </div>
            </div>
        );
    

【问题讨论】:

你试过this.setState(prevState =&gt; counter: prevState.counter + 1);。这样您就不会丢失先前状态的当前值。 【参考方案1】:

设置状态是异步的,因此 react 可以通过批量调用 setState 来做一些性能优化。因此,依赖于先前状态值的状态更改(即用于递增计数器)您应该使用setState 的函数版本 https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

您在调用 setState 后立即记录,但由于它是异步的,因此您正在记录的状态可能 实际上 还没有被 react 设置。您可以改为将其记录在回调中,保证在状态实际上已在表面下更新后运行

顺便说一句,如果答案正确与否,您的代码的作用完全相同,只是它增加而不是减少分数,因此我们可以将其稍微干燥到单个 setState 中,并带有条件增量值

handleClickCont(choice) 
  this.setState(prevState => (
      scorekeeper: prevState.scorekeeper + (choice === this.props.answer ? 1 : -1),
      counter: prevState.counter + 1,
      hideDiv: !prevState.hideDiv
    ),
    () =>  // callback, guaranteed to have done your updates by this point
      console.log('scorekeeper ' + this.state.scorekeeper)
      console.log('counter ' + this.state.counter)
    
  )

【讨论】:

您好,谢谢您的回复。这对我的问题是一个非常有用的解释:)。但是,我用您在此处给出的代码替换了我原来的 handleClickCont 函数,当我第一次回答问题时,状态的记分员和计数器部分会正确更新。但是,第二次及以后的时间将控制台记录 1 保持在两个状态的值... 所以这听起来像是一个不同的问题,其中 QuestionCard 是一个不同的实例,可能是从被卸下然后用不同的问题道具重新安装。在这种情况下,状态计数器将再次从 0 开始,因此您最多只能达到 1。必须看看是什么控制了这些问题以及呈现了什么&lt;QuestionCard /&gt; 才能确定原因 此组件中没有任何内容可以在回答时更改卡片中的问题,实际上您在回答时将其完全隐藏,这让我认为您只是有 4 个并排单独的 QuestionCard 实例,在这种情况下他们对彼此的状态一无所知。状态包含在单个实例中 这个分数/计数器状态应该在父级中,它知道所有问题卡,问题卡本身具有触发状态设置的onAnswered(correct: boolean): void 属性 是的,没错,我有 4 个并排单独的 QuestionCard 实例。我将状态的分数和计数器部分移动到父组件,现在它工作得很好。非常感谢!!【参考方案2】:

记住 this.setState 就像 return 语句。可能不会执行 setstate 之后的所有语句。一旦你设置了组件的渲染方法就会被调用,除非你指定不渲染。更进一步的语句可能不会被执行。

handleClickCont(choice) 
    if(choice === this.props.answer) 
        this.setState(scorekeeper: this.state.scorekeeper + 1,counter: this.state.counter + 1,hideDiv: !this.state.hideDiv);
     else

    this.setState(scorekeeper: this.state.scorekeeper - 1,counter: this.state.counter + 1,hideDiv: !this.state.hideDiv);
    


你也不能返回 this.setState。调用 setState 时也不总是执行它。 react 可能会对其进行批处理并稍后执行。

【讨论】:

以上是关于反应:组件状态不更新记分员或计数器的主要内容,如果未能解决你的问题,请参考以下文章

如何使用我的反应应用程序中的 useState 钩子给出的 setState 方法增加状态对象的选定索引的计数

React useContext,useRedux子组件不更新

更新的状态值不会在反应中的函数内部更新

页面重新渲染/更改后如何维护组件状态?

React - 更新每个子组件的状态

如何在 Reactjs 中给两个组件自己的状态?