一、setState() 的使用
setState()
更新状态的两种写法,如下所示:
setState(updater, [callback])
,updater
为返回stateChange
对象的函数:(state, props) => stateChange
,接收的state
和props
被保证为最新的setState(stateChange, [callback])
,stateChange
为对象,callback
是可选的回调函数,在状态更新且界面更新后才执行
- 对于
setState()
写法的总结,如下所示:
- 对象方式是函数方式的简写方式
- 如果新状态不依赖于原状态,那么就使用对象方式
- 如果新状态依赖于原状态,那么就使用函数方式
- 如果需要在
setState()
后获取最新的状态数据,在第二个callback
函数中读取
- 对于
setState()
的简单应用,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>setState()</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>
<script type="text/babel">
class A extends React.Component {
state = {
count: 1
}
test1 = () => {
this.setState(state => ({count: state.count + 1}))
console.log('test1 setState()之后', this.state.count)
}
test2 = () => {
/*const count = this.state.count + 1
this.setState({
count
})*/
this.setState({
count: 3
})
console.log('test2 setState()之后', this.state.count)
}
test3 = () => {
this.setState(state => ({count: state.count + 1}), () => { // 在状态更新且界面更新之后回调
console.log('test3 setState callback()', this.state.count)
})
}
render() {
console.log('A render()')
return (
<div>
<h1>A组件: {this.state.count}</h1>
<button onClick={this.test1}>A 测试1</button>
<button onClick={this.test2}>A 测试2</button>
<button onClick={this.test3}>A 测试3</button>
</div>
)
}
}
ReactDOM.render(<A/>, document.getElementById('example'))
</script>
</body>
</html>
二、setState() 更新状态的同步与异步
- 在
react
中,执行setState()
的位置,如下所示:
- 在
react
控制的回调函数中,有生命周期勾子函数、react
事件监听回调等等 - 非
react
控制的异步回调函数中,有定时器回调、原生事件监听回调、promise
回调等等
- 在
react
中,setState()
的同步与异步,如下所示:
react
相关回调中是异步的- 其它异步回调中是同步的
- 对于异步的
setState()
,在多次调用时进行处理,如下所示:
setState({})
: 合并更新一次状态,只调用一次render()
更新界面,状态更新和界面更新都合并了setState(fn)
: 更新多次状态, 但只调用一次render()
更新界面 ,状态更新没有合并, 但界面更新合并了
-
对于异步的
setState()
,得到异步更新后的状态数据,可以在setState()
的callback
回调函数中 -
对于
setState()
更新状态的异步与同步的应用,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>setState()</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>
<script type="text/babel">
class StateTest extends React.Component {
state = {
count: 0,
}
/*
react事件监听回调中, setState()是异步更新状态
*/
update1 = () => {
console.log('update1 setState()之前', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('update1 setState()之后', this.state.count)
}
/*
react生命周期勾子中, setState()是异步更新状态
*/
componentDidMount () {
console.log('componentDidMount setState()之前', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('componentDidMount setState()之后', this.state.count)
}
/*
定时器回调 / 原生事件监听回调 / promise回调 /...
*/
update2 = () => {
setTimeout(() => {
console.log('setTimeout setState()之前', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('setTimeout setState()之后', this.state.count)
})
}
update3 = () => {
const h2 = this.refs.count
h2.onclick = () => {
console.log('onclick setState()之前', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('onclick setState()之后', this.state.count)
}
}
update4 = () => {
Promise.resolve().then(value => {
console.log('Promise setState()之前', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('Promise setState()之后', this.state.count)
})
}
update5 = () => {
console.log('onclick setState()之前', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('onclick setState()之后', this.state.count)
console.log('onclick setState()之前2', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('onclick setState()之后2', this.state.count)
}
update6 = () => {
console.log('onclick setState()之前', this.state.count)
this.setState({count: this.state.count + 1})
console.log('onclick setState()之后', this.state.count)
console.log('onclick setState()之前2', this.state.count)
this.setState({count: this.state.count + 1})
console.log('onclick setState()之后2', this.state.count)
}
update7 = () => {
console.log('onclick setState()之前', this.state.count)
this.setState({count: this.state.count + 1})
console.log('onclick setState()之后', this.state.count)
console.log('onclick setState()之前2', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('onclick setState()之后2', this.state.count)
}
render() {
const {count} = this.state
console.log('render()', count)
return (
<div>
<h2 ref='count'>{count}</h2>
<button onClick={this.update1}>更新1</button> ---
<button onClick={this.update2}>更新2</button>
<button onClick={this.update3}>更新3</button>
<button onClick={this.update4}>更新4</button> ---
<button onClick={this.update5}>更新5</button>
<button onClick={this.update6}>更新6</button>
<button onClick={this.update7}>更新7</button>
</div>
)
}
}
ReactDOM.render(<StateTest/>, document.getElementById('example')) // 渲染组件标签, 内部会调用组件标签对象的render()虚拟DOM
</script>
</body>
</html>
三、setState() 的面试题
- 对于
setState()
的面试题,代码如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>setState()</title>
</head>
<body>
<div id="example"></div>
<script type="text/javascript" src="./js/react.development.js"></script>
<script type="text/javascript" src="./js/react-dom.development.js"></script>
<script type="text/javascript" src="./js/babel.min.js"></script>
<script type="text/babel">
class StateTest extends React.Component {
state = {
count: 0,
}
componentDidMount() {
this.setState({count: this.state.count + 1})
this.setState({count: this.state.count + 1})
console.log(this.state.count) // 2 ==> 0
this.setState(state => ({count: state.count + 1}))
this.setState(state => ({count: state.count + 1}))
console.log(this.state.count) // 3 ==> 0
setTimeout(() => {
this.setState({count: this.state.count + 1})
console.log('timeout', this.state.count) // 10 ==> 6
this.setState({count: this.state.count + 1})
console.log('timeout', this.state.count) // 12 ==> 7
}, 0)
Promise.resolve().then(value => {
this.setState({count: this.state.count + 1})
console.log('promise', this.state.count) // 6 ==>4
this.setState({count: this.state.count + 1})
console.log('promise', this.state.count) // 8 ==> 5
})
}
render() {
const count = this.state.count
console.log('render', count) // 1 ==> 0 4 ==>3 5 ==>4 7 ==>5 9 ==>6 11 ==>7
return (
<div>
<p>{count}</p>
</div>
)
}
}
ReactDOM.render(<StateTest/>, document.getElementById('example'))
</script>
</body>
</html>