React this.setState
搞前端的小菜 人气:0摘要
这一篇文章,主要是简单的实现一下this.setState方法,为了实现该方法,就要知道this.setState方法具有什么特点。
首先在React组件中,我们先定义一个state和setState方法:
myState = { value: 0 } mySetState = ( changeState ) =>{ this.setState( this.myState ) }
这里可能会说,为什么在自己写的mySetState 方法里还要调用React的setState呢?都调用人家的了还算自己写的吗?
由于在React中,render只能处理通过setState方法修改的值,所以这里我们在mySetState 中调用了一下。但是mySetState方法的具体实现还是我们自己去完成。
1.异步的setState
首先,我们看一段代码:
state = { value: 0 }
setTimeout(() => { console.log('SetTimeOut ---- '+this.state.value); }, 0); new Promise((resolve,reject) => { resolve(this.state.value) }) .then(res => { console.log('Promise ---- '+res); }) this.setState({ value: this.state.value+1 }) console.log(this.state.value);
这段代码会输出什么呢?
由这个结果我们可以知道上面的代码执行顺序:
(1)console.log(this.state.value);
(2)Promise代码
(3)setState方法
(4)setTimeOut方法
所以setState一定是一个异步的方法。
在实现的时候,要注意异步的问题。
2.多个setState方法
我们继续看一段代码:
this.setState({ value: this.state.value+1 }) this.setState({ value: this.state.value+1 }) this.setState({ value: this.state.value+1 }) this.setState({ value: this.state.value+1 }) this.setState({ value: this.state.value+1 }) setTimeout(() => { console.log(this.state.value); }, 0);
这段代码输出的会是1还是5呢?
答案是1,这是React为了效率所作的一个优化。防止每次setState都会导致render重新渲染!
而如果我就想要这个效果,React也是提供了一个解决办法,就是setState方法可以接受一个函数作为参数:
this.setState( (preState) => { return { value: preState.value+1 } } ) this.setState( (preState) => { return { value: preState.value+1 } } ) this.setState( (preState) => { return { value: preState.value+1 } } ) this.setState( (preState) => { return { value: preState.value+1 } } ) setTimeout(() => { console.log(this.state.value); }, 0);
preState代表的是上一个state。
3.手动实现mySetState
OK,有了上面对setState方法分了解,我们可以手动的实现一下mySetState方法
首先解决就是调用多个setState方法的时候,所以我们不能每次调用mySetState方法都让state值更新,也就是这么写:
mySetState = ( changeState ) =>{ Object.assign(this.myState,changeState) this.setState( this.myState ) }
我们只需要知道最后一个changeState,所以这里我们维护一个队列,用来保存所有的changeState,然后每次调用mySetState 方法的时候,将changeState放到队列里面:
queue = [] mySetState = ( changeState ) =>{ Promise.resolve().then(()=>{ this.stateShift() }) this.queue.push(changeState) this.setState( this.myState ) }
重头戏来了,我们已经有了这个队列,那我们是如何让这个队列的changeState 出来的呢?
这里我们写一个方法:
stateShift() { let empty; while(empty = this.queue.shift()){ } }
通过迭代器的方式,遍历队列。empty就是拿到的每个changeState 。changeState 有两种形式,对象的时候肯定是很好写的。
stateShift() { let empty; while(empty = this.queue.shift()){ if(typeof empty === 'object'){ Object.assign(this.myState,empty) } }
只需要让新出来的去替换旧的就可以了。如果changeState 是一个方法,这个时候,我们需要手动去维护一个preState变量,这个变量的作用就是用来保存上一个state。
所以具体的实现应该为:
stateShift() { let empty; while(empty = this.queue.shift()){ if(!this.preState){ this.preState = Object.assign({},this.myState) } if(typeof empty === 'object'){ Object.assign(this.myState,empty) }else if(typeof empty === 'function'){ Object.assign(this.myState,empty(this.preState)) } this.preState = this.myState } }
最后一步,这个方法应该什么时候调用。其实需要注意的无非就是,要在所有的changeState 都放到队列中,再进行调用。
而this.queue.push(changeState)这段代码又是同步的代码,所以在它之前,通过异步的方式调用,就可以实现出这种效果:
mySetState = ( changeState ) =>{ Promise.resolve().then(()=>{ this.stateShift() }) this.queue.push(changeState) this.setState( this.myState ) }
最后将整个实现代码附上:
import React, { Component } from 'react' export default class Child extends Component { state = { value: 0 } myState = { value: 0 } queue = [] mySetState = ( changeState ) =>{ Promise.resolve().then(()=>{ this.stateShift() }) this.queue.push(changeState) this.setState( this.myState ) } stateShift() { let empty; while(empty = this.queue.shift()){ if(!this.preState){ this.preState = Object.assign({},this.myState) } if(typeof empty === 'object'){ Object.assign(this.myState,empty) }else if(typeof empty === 'function'){ Object.assign(this.myState,empty(this.preState)) } this.preState = this.myState } } add = ()=>{ //测试代码 // this.mySetState( (pre) => { // return { // value: pre.value + 1 // } // } ) // this.mySetState( (pre) => { // return { // value: pre.value + 1 // } // } ) // this.mySetState( (pre) => { // return { // value: pre.value + 1 // } // } ) // this.mySetState({ // value: this.myState.value+1 // }) // this.mySetState({ // value: this.myState.value+1 // }) // this.mySetState({ // value: this.myState.value+1 // }) // this.mySetState({ // value: this.myState.value+1 // }) setTimeout(() => { console.log('SetTimeOut ---- '+this.myState.value); }, 0); new Promise((resolve,reject) => { resolve(this.myState.value) }) .then(res => { console.log('Promise ---- '+res); }) this.mySetState({ value: this.myState.value+1 }) console.log(this.myState.value); } render() { return ( <div> <span>{this.myState.value}</span> <br></br> <button onClick={this.add}>++</button> </div> ) } }
最后值得注意的是,这里只是针对于setState的一些特点去模拟了一下setState的实现,具体的源码可能不会像这样简单!
加载全部内容