Open shoutingwei opened 5 years ago
首先我们来看一个栗子:
class Eg extends Component { contructor () { super() this.state = { value: 0, index: 0 } } componentDidMount () { this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第一次输出 this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第二次输出 setTimeout(() => { this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第三次输出 this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第四次输出 }, 0); this.refs.button.addEventListener('click', this.click) } click = () => { this.setState({value: this.state.index + 1}) this.setState({value: this.state.index + 1}) } render () { return ( <div><span>value: {this.state.value}index: {this.props.index}</span> <button ref="button" onClick={this.click}>点击</button> </div> ) } }
这四次输出,按常理来说分别是: 1,2,3,4。但是,实际输出为: 0, 0, 2, 3。
在分析之前,需要知道setState的一些关键点。 setState的关键点 setState不会立刻改变React组件中state的值
setState通过引发一次组件的更新过程来引发重新绘制 重绘指的就是引起React的更新生命周期函数4个函数:
shouldComponentUpdate(被调用时this.state没有更新;如果返回了false,生命周期被中断,虽然不调用之后的函数了,但是state仍然会被更新) componentWillUpdate(被调用时this.state没有更新)
componentDidUpdate
多次setState函数调用产生的效果会合并。
this.setState({name: 'Pororo'}) this.setState({age: 20}) this.setState({name: 'Pororo',age: 20}) 上面两块代码的效果是一样的。如果每次调用
都引发一次生命周期更新,那性能就会消耗很大了。所以,React会将多个this.setState产生的修改放进一个队列里,等差不多的时候就会引发一次生命周期更新。
分析 知道了这些之后就开始分析。
先来分析前两次
setState:this.setState({value: this.state.val + 1}); console.log(this.state.value); // 第一次输出 this.setState({value: this.state.val + 1}); console.log(this.state.value); // 第二次输出
由于setState不会立即改变React组件中state的值,所以两次setState中this.state.value都是同一个值0,故而,这两次输出都是0。因而value只被加1。 既然这样,那么是不是可以直接操作this.state呢?比如:### this.state.value=this.state.value+1;这样的确可以修改this.state.value的状态但是却不可以引发重复渲染。所以,就必须通过React设定的setState函数去改变this.state,从而引发重新渲染。 接下来分析setTimeout里面的两次setState:
setTimeout(() => { this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第三次输出 this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第四次输出 }, 0);
这两次this.stat的值同步更新了,这是为什么的呢?
那为什么会这样呢?
同步更新state的办法—函数式setState如果this.setState的参数不是一个对象而是一个函数时,这个函数会接收到两个参数,第一个是当前的state值,第二个是当前的props,这个函数应该返回一个对象,这个对象代表想要对this.state的更改,换句话说,之前你想给this.setState传递什么对象参数,在这种函数里就返回什么对象。不过,计算这个对象的方法有些改变,不再依赖于this.state,而是依赖于输入参数state。
function increment(state, props) { return {count: state.count + 1}; } function incrementMultiple() { this.setState(increment); this.setState(increment); this.setState(increment); }
对于多次调用函数式setState的情况,React会保证调用每次increment时,state都已经合并了之前的状态修改结果。**
要注意的是,### 在increment函数被调用时,this.state并没有被改变,依然,要等到render函数被重新执行时(或者shouldComponentUpdate函数返回false之后)才被改变。
把两种setState的用法混用,会有什么效果?我们把incrementMultiple改成这样。
function incrementMultiple() { this.setState(increment); this.setState(increment); this.setState({count: this.state.count + 1}); this.setState(increment); }
在几个函数式setState调用中插入一个传统式setState调用,最后得到的结果是让this.state.count增加了2,而不是增加4。 这是因为React会依次合并所有setState产生的效果,### 虽然前两个函数式setState调用产生的效果是count加2,但是中间出现一个传统式setState调用,一下子强行把积攒的效果清空,用count加1取代。 所以,### 传统式setState与函数式setState一定不要混用。 总结自setState何时同步更新状态、setState为什么不会同步更新组件状态
作者:不洗碗工作室 链接:https://juejin.im/post/5a155f906fb9a045284622b4
不用混用的话 是在一个周期内部不能还是在全部周期内都不能?
首先我们来看一个栗子:
这四次输出,按常理来说分别是: 1,2,3,4。但是,实际输出为: 0, 0, 2, 3。
在分析之前,需要知道setState的一些关键点。 setState的关键点 setState不会立刻改变React组件中state的值
setState通过引发一次组件的更新过程来引发重新绘制 重绘指的就是引起React的更新生命周期函数4个函数:
shouldComponentUpdate(被调用时this.state没有更新;如果返回了false,生命周期被中断,虽然不调用之后的函数了,但是state仍然会被更新) componentWillUpdate(被调用时this.state没有更新)
render(被调用时this.state得到更新)
componentDidUpdate
多次setState函数调用产生的效果会合并。
都引发一次生命周期更新,那性能就会消耗很大了。所以,React会将多个this.setState产生的修改放进一个队列里,等差不多的时候就会引发一次生命周期更新。
分析 知道了这些之后就开始分析。
先来分析前两次
由于setState不会立即改变React组件中state的值,所以两次setState中this.state.value都是同一个值0,故而,这两次输出都是0。因而value只被加1。 既然这样,那么是不是可以直接操作this.state呢?比如:### this.state.value=this.state.value+1;这样的确可以修改this.state.value的状态但是却不可以引发重复渲染。所以,就必须通过React设定的setState函数去改变this.state,从而引发重新渲染。 接下来分析setTimeout里面的两次setState:
这两次this.stat的值同步更新了,这是为什么的呢?
在React中,如果是由React引发的事件处理(比如:onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.setState。 “除此之外”指的是:### 绕过React通过addEventListener直接添加的事件处理函数和setTimeout/setInterval产生的异步调用。
那为什么会这样呢?
每次setState产生新的state会依次被存入一个队列,然后会根据isBathingUpdates变量判断是直接更新this.state还是放进dirtyComponent里回头再说。isBatchingUpdates默认是false,也就表示setState会同步更新this.state。但是,当React在调用事件处理函数之前就会调用batchedUpdates,这个函数会把isBatchingUpdates修改为true,造成的后果就是由React控制的事件处理过程setState不会同步更新this.state。
同步更新state的办法—函数式setState如果this.setState的参数不是一个对象而是一个函数时,这个函数会接收到两个参数,第一个是当前的state值,第二个是当前的props,这个函数应该返回一个对象,这个对象代表想要对this.state的更改,换句话说,之前你想给this.setState传递什么对象参数,在这种函数里就返回什么对象。不过,计算这个对象的方法有些改变,不再依赖于this.state,而是依赖于输入参数state。
**假如当前this.state.count的值是0,第一次调用this.setState(increment),传给increment的state参数是0,第二调用时,state参数是1,第三次调用是,参数是2,最终incrementMultiple让this.state.count变成了3。
对于多次调用函数式setState的情况,React会保证调用每次increment时,state都已经合并了之前的状态修改结果。**
要注意的是,### 在increment函数被调用时,this.state并没有被改变,依然,要等到render函数被重新执行时(或者shouldComponentUpdate函数返回false之后)才被改变。
把两种setState的用法混用,会有什么效果?我们把incrementMultiple改成这样。
在几个函数式setState调用中插入一个传统式setState调用,最后得到的结果是让this.state.count增加了2,而不是增加4。 这是因为React会依次合并所有setState产生的效果,### 虽然前两个函数式setState调用产生的效果是count加2,但是中间出现一个传统式setState调用,一下子强行把积攒的效果清空,用count加1取代。 所以,### 传统式setState与函数式setState一定不要混用。 总结自setState何时同步更新状态、setState为什么不会同步更新组件状态
作者:不洗碗工作室 链接:https://juejin.im/post/5a155f906fb9a045284622b4