组件的state和setState
setState
方法由父类 Component
所提供。当我们调用这个函数的时候,React.js 会更新组件的状态 state
,并且重新调用 render
方法,然后再把 render
方法所渲染的最新的内容显示到页面上。
注意,当我们要改变组件的状态的时候,不能直接用 this.state = xxx
这种方式来修改,如果这样做 React.js 就没办法知道你修改了组件的状态,它也就没有办法更新页面。在Vue中可以通过 this.state = xxx
的形式去修改,但在React中要调整过来,为的是使得数据更改与页面更新渲染同步。
setState 接受对象参数
传入的对象代表了组件的新状态,但我们其实只需要传入修改的部分即可,不需要传入整个对象。如下:
constructor (props) {
super(props)
this.state = {
name: 'Tomy',
isLiked: false
}
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
// 只修改isLiked
})
}
setState 接受函数参数
调用setState的时候,React.js 并不会马上修改 state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state
当中,然后再触发组件更新。
handleClickOnLikeButton () {
console.log(this.state.isLiked)
this.setState({
isLiked: !this.state.isLiked
})
console.log(this.state.isLiked)
}
两次打印的都是 false
,即使我们中间已经 setState
过一次了。
React.js 的 setState
把你的传进来的状态缓存起来,稍后才会帮你更新到 state
上,所以你获取到的还是原来的 isLiked
。
这就有点像异步代码了,所以在setState之后使用新的state去做运算就做不到了。于是需要接受函数参数的做法:
handleClickOnLikeButton () {
this.setState((prevState) => {
return { count: 0 }
})
this.setState((prevState) => {
return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1
})
this.setState((prevState) => {
return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3
})
// 最后的结果是 this.state.count 为 3
}
这种做法可以做到利用上一次setState的结果进行运算,有变成了一个同步代码的过程。
关于setState的合并
上述代码虽然执行三次setState
,但实际上只会重新渲染一次。在React.js内部会把JavaScript的Event Loop中的消息队列的同一个消息中的setState
进行合并,之后再重新进行组件的渲染。这里还不需要了解太多深层的原理。在开发中我们只需要记住:使用React.js的时候,并不需要担心多次进行 setState
会带来性能问题。
课后练习
class Dog extends Component {
constructor () {
super()
/* TODO */
this.state={
isRunning:false,
isBarking:false
}
}
bark () {
/* TODO */
this.setState({ isBarking: true })
setTimeout(() => this.setState({ isBarking: false }), 20)
}
run () {
/* TODO */
this.setState({ isRunning: true })
setTimeout(() => this.setState({ isRunning: false }), 20)
}
handleClick(){
this.bark();
this.run();
}
render () {
return (<div onClick={this.handleClick.bind(this)}>DOG</div>)
}
}