React的state原理

629 阅读3分钟

不能直接修改state

state是个不可变对象,直接修改state,并不会重新触发render。例如:

//错误
this.state.title='attend';
//正确
this.setState({title:'attend'});

setState的原理

  1. 根据原来的this.state和传进来的参数来判断是否批量更改;
  2. 再根据this.state来计算nextstate。有变化,就返回一个新对象;没变化,直接返回原对象;
  3. shouldComponentUpdate()根据拿到的nextstate来返回一个布尔值,true则进行下一步(此处一定是true,所以setState一定会往下执行),然后就进入componentWillUpdate;
  4. render()生成一个虚拟DOM;
  5. 根据diff算法进行更新渲染,结束后进入生命周期函数componentDidUpdate()。

state的更新是异步的

调用setState时,组件state并不会立即改变,而是把要修改的状态放入事件队列当中。
如果频繁修改setState,会将多次setState的状态修改合并成一次状态修改。

state更新时不是直接替换,而是只修改发生变化的值

通过setState()修改组件的状态时,只需要传入发生改变的state,而不是完整的state。

this.state = {
    title: 'this is title',
    content: 'this is content'
}

当只需要修改title时,只需要将修改的title传给setState即可:
this.setState({ title:'this is subTitle' });

react会合并最新的title到原来的状态,同时保留原来状态的content,最终合并state为:

this.state = {
    title: 'this is subTitle',
    content: 'this is content'
}

state中包含的所有状态都是不可变的对象

state当中的某一个状态发生变化时,会重新创建这个状态对象,而不是直接修改原来的state状态。
创建新状态的关键是,避免直接修改原对象,而是使用可以返回一个新对象的方法。

我们根据状态类型可以分为下面三种情况:

  • 状态类型为不可变类型(number、string、boolean、null、undefined)
    直接给要修改的状态赋一个新值。
  • 状态类型为数组
    假如有一个数组类型的状态 arr ,然后向 arr 中增加一个元素。
//方法一:使用数组的concat方法,创建新数组
this.setState(prevState=>({
    arr: prevState.arr.concat(['new element'])
}))

//方法二:ES6的对象扩展语法
this.setState(prevState=>({
    arr: [...prevState, 'new element']
}))

//方法三:当我们从 arr 中截取部分元素作为新状态时,可以用数组的slice方法:
this.setState(prevState=>({
    arr: prevState.arr.slice(1,3);
}))

//方法四:当从 arr 中过滤部分元素后,作为新状态时,可以使用filter方法:
this.setState(prevState=>({
    arr: prevState.arr.filter(item=>{
        return item!='new element';
    })
}))

【注意】不要使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改的,而concat,slice,filter会返回一个新的数组。

  • 状态的类型是普通对象
//方法一:使用ES6的Object.assgin()方法:
this.setState({
    onwer: Object.assgin({},preState.onwer,{name:'Jason'});
})

//方法二:使用ES6的对象扩展语法:
this.setState(preState=>{
    owner: {...preState.owner, name:'Jason'}
})

【进阶】React中,组件状态不可变对象的原因:
(1) 不可变对象的修改会返回一个新的对象,不用担心原对象在不小心的情况下修改导致的错误,方便程序的管理和调试。
(2) 出于性能的考虑,对象组件的状态是不可变对象时,在组件的shouldComponentUpdate方法中仅需要比较前后两次状态对象的引用就可以判断状态是否真的改变,从而避免不必要的render调用。
【进阶】replaceState
replaceState 方法与 setState 类似,但是方法只会保留nextState中状态,原state不在nextState中的状态都会被删除,即直接替换原state。