React的状态更新问题,请老司机看一眼,多谢!

我初学react写了一个小组件,在更新组件状态的时候发现计算数据有些不正确。请教一下。
▼如下图:
图片描述

目的是每增加一个苹果,苹果数量增加一个,同时更新总重&总计金额。
目前问题是,从0增加到1个苹果时,合计金额没有变化。请帮看一下哪里的问题。谢谢老司机。
good man one life safe.
▼部分相关代码如下

<body>
<div id="div01">
    插件未正确加载
</div>
<script type="text/babel">
    var BuyApple = React.createClass({
        render: function () {
            return (
                    <div >
                        <p>
                            购买<span>{this.state.shuLiang}</span>个苹果,
                            每个<span>{this.state.danZhong}</span>斤,
                            每斤<span>{this.state.danJia}</span>元,
                            一共<span>{this.state.zongZhong}</span>斤,
                            合计<span>{this.state.zongJi}</span>元。
                        </p>
                        <input type="button" value="增加苹果" onClick={ this.addApple}/>
                        <input type="button" value="减少苹果" onClick={ this.reduceApple}/>
                        <input type="button" value="增加单价" onClick={ this.addMoney}/>
                        <input type="button" value="减少单价" onClick={ this.reduceMoney}/>
                    </div>
            );
        },
        getInitialState:function () {
            return{
                shuLiang: 0, //数量
                danZhong: 0.5, //单重
                danJia:2, //单价
                zongZhong: 0, //总重
                zongJi: 0, //总计
            }
        },
        addApple:function () {
            this.setState({ //▼问题八成出在这里
                shuLiang: ++this.state.shuLiang, //苹果数量加1
                zongZhong: this.state.shuLiang * this.state.danZhong, //苹果总重=数量*单重
                zongJi: this.state.zongZhong * this.state.danJia, //总计=总重*单价
            });
        },
    });
    ReactDOM.render(
        <BuyApple/>,
        document.querySelector('#div01'),
    );
</script>
</body>
阅读 4.3k
9 个回答

最主要的原因是setState是个异步的操作

this.setState({ 
           shuLiang: ++this.state.shuLiang, //苹果数量加1
           //问题出在设置zongZhong的时候拿到的shuLiang是旧的
           zongZhong: this.state.shuLiang * this.state.danZhong, //苹果总重=数量*单重
           zongJi: this.state.zongZhong * this.state.danJia, //总计=总重*单价
            });

两个方案

  1. 按照楼上说的,总重在render里去算

  2. setState有第二个参数,是个callback,可以拿到新的state,你可以在这个callback里去设置总重,甩一个文档

当然,我建议你用第一种方案

你在同一个setState里设置数量和总重,实际上总重调用的this.state.shuLiang还没改变,还是之前的值。压根就不用保存太多的state。

<p>
    购买<span>{this.state.shuLiang}</span>个苹果,
    每个<span>{this.state.danZhong}</span>斤,
    每斤<span>{this.state.danJia}</span>元,
    一共<span>{this.state.shuLiang * this.state.danZhong}</span>斤,
    合计<span>{this.state.shuLiang * this.state.danZhong * this.state.danJia}</span>元。
</p>

state里面要存最少的值,能通过计算出来的都不能存

这个zongJi不要放在state里面,在展示的时候直接算出来就行了。

1:state要使用最小状态集合

 return{
                shuLiang: 0, //数量
                danZhong: 0.5, //单重
                danJia:2, //单价
                zongZhong: 0, //总重
                zongJi: 0, //总计
            }

明显,zongZhong和zongJi是可以通过前面三个state计算出来的。

2:建议在render里计算,不要在setState里是算。

3:问题的根本原因是@zhangfe的解释,为了性能优化,React的setState(new State, callback)是异步的,他甩的文档里说得很清楚。

没用过react,但是我猜测问题大概是这样的。我认为setState方法大概是这样的

this.setState = function(obj) {
    for(var key in obj) {
        this.state[key] = obj[key];
    }
    ...
}

而你的代码运行过程实际上是

var obj = {
    shuLiang: ++this.state.shuLiang, //苹果数量加1
    zongZhong: this.state.shuLiang * this.state.danZhong, //苹果总重=数量*单重
    zongJi: this.state.zongZhong * this.state.danJia, //总计=总重*单价
}

然后再
this.setState(obj);
这时候obj里面的值已经计算完了,所以导致的结果不正确。
所以你可以

var obj = {};
obj.shuLiang = this.state.shuLiang + 1;
obj.zongZhong = obj.shuLiang * this.state.danZhong;
obj.zongJi = obj.zongZhong * this.state.danJia;
this.setState(obj);

....多看下文档,很多api后面都不止我们常用的一个参数,还有能不能用英文,拼音看着挺别扭的。养成个好习惯嘛,大家共勉。

其实吧,使用定时器是个很好的选择,在didMount的时候调用计时器,在willMount的时候清空计时器

按照官方文档,state应该保存尽量少的变量,其它可以算出来的再在render函数中更新

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题