六、State
state
是 class
组件的内置对象,用于class组件内部数据更新
state
就是组件描述某种显示情况的数据,由组件自己设置和更改,也就是说由组件自己维护,使用state
的目的就是为了在不同的状态下使组件的显示不同(自己管理)
6.1 state及其特点
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件
不要直接修改state
state更新可能是异步的:出于性能考虑,React 可能会把多个 setState()
调用合并成一个调用。
state更新会被合并:当你调用 setState()
的时候,React 会把你提供的对象合并到当前的 state
6.2 state的定义和使用
目前react中的状态有两种使用方式:
6.2.1 es6的类 - 构造函数
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './01_props/01_Parent_Child' // 父子组件
// import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值
// import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值
// import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型
// import App from './01_props/05_App_props_children' // 类插槽
// import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽
// import App from './01_props/07_App_mouse_tracker' // 鼠标跟随
// import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态
import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/02_state/01_App_state_es6.jsx
// src/02_state/01_App_state_es6.jsx
import React, { Component } from 'react';
export default class App extends Component {
constructor (props) {
super(props)
this.state = {
date: new Date()
}
}
render() {
return (
<div>
当前时间为:
{ this.state.date.toLocaleDateString() } -
{ this.state.date.toLocaleTimeString() }
</div>
);
}
}
6.2.2 es7的类 - 属性初始化器
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './01_props/01_Parent_Child' // 父子组件
// import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值
// import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值
// import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型
// import App from './01_props/05_App_props_children' // 类插槽
// import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽
// import App from './01_props/07_App_mouse_tracker' // 鼠标跟随
// import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态
// import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数
import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/02_state/02_App_state_es7.jsx
// src/02_state/02_App_state_es7.jsx
import React, { Component } from 'react'
export default class App extends Component {
// es7 的属性初始化器
state = {
date: new Date()
}
render() {
return (
<div>
<div>
当前时间为:
{ this.state.date.toLocaleDateString() } -
{ this.state.date.toLocaleTimeString() } !
</div>
</div>
)
}
}
6.3 如何正确的修改state
setState()
将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式.
将 setState()
视为请求而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。
setState()
并不总是立即更新组件。它会批量推迟更新。这使得在调用 setState()
后立即读取 this.state
成为了隐患。为了消除隐患,请使用 componentDidUpdate
或者 setState
的回调函数(setState(updater, callback)
),这两种方式都可以保证在应用更新后触发。
记住修改状态的三大原则:
-
不要直接修改 State
state = { a: 10 }
this.state.a = 100 // ❌ -
state 的更新可能是异步的
state = { a: 10 }
this.setState({a: this.state.a + 1 })
this.setState({a: this.state.a + 1 })
this.setState({a: this.state.a + 1 })
console.log(this.state.a) // 10 -
state 的更新会被合并
6.4 this.setState()方法及其特点
setState()
会对一个组件的 state
对象安排一次更新。当 state 改变了,该组件就会重新渲染。
setState()
可以添加两个参数,
setState()
的第二个参数为可选的回调函数,它将在 setState
完成合并并重新渲染组件后执行
6.4.1 传递函数
参数一为带有形式参数的 updater
函数:
this.setState((state, props) => stateChange[, callback] )
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './01_props/01_Parent_Child' // 父子组件
// import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值
// import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值
// import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型
// import App from './01_props/05_App_props_children' // 类插槽
// import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽
// import App from './01_props/07_App_mouse_tracker' // 鼠标跟随
// import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态
// import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数
// import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器
import App from './02_state/03_App_setState_function' // 修改状态 传递函数
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/02_state/03_App_setState_function.jsx
// src/02_state/03_App_setState_function.jsx
import React, { Component } from 'react';
export default class App extends Component {
state = {
count: 0
}
render() {
return (
<div>
<button onClick={ () => {
this.setState((state, props) => { // state.count 0
return {
count: state.count + 1
}
}, () => {
console.log('1', this.state.count) // 3
})
this.setState((state, props) => { // state.count 1
return {
count: state.count + 1
}
}, () => {
console.log('2', this.state.count) // 3
})
this.setState((state, props) => { // state.count 2
return {
count: state.count + 1
}
}, () => {
console.log('2', this.state.count) // 3
})
} }>加1</button>
{ this.state.count }
</div>
);
}
}
updater 函数中接收的
state
和props
都保证为最新。updater 的返回值会与state
进行浅合并。
6.4.2 传递对象
src/index.js
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入react组件时,后缀名可以不写
// import App from './01_props/01_Parent_Child' // 父子组件
// import App from './01_props/02_Parent_Child_value' // 父组件给子组件传值
// import App from './01_props/03_Parent_Child_default' // 父组件给子组件传值,子组件设置默认值
// import App from './01_props/04_Parent-Child_type' // 父组件给子组件传值,子组件设置默认值,验证数据类型
// import App from './01_props/05_App_props_children' // 类插槽
// import App from './01_props/06_App_mutiple_props_children' // 类具名插槽 多个插槽
// import App from './01_props/07_App_mouse_tracker' // 鼠标跟随
// import App from './01_props/08_App_render_props' // 渲染属性 - 其他组件共享状态
// import App from './02_state/01_App_state_es6' // 初始化状态 - es6的构造函数
// import App from './02_state/02_App_state_es7' // 初始化状态 - es7 属性初始化器
// import App from './02_state/03_App_setState_function' // 修改状态 传递函数
import App from './02_state/04_App_setState_object' // 修改状态 传递对象
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App />)
src/02_state/04_App_setState_object.jsx
// src/02_state/04_App_setState_object.jsx
import React, { Component } from 'react'
export default class App extends Component {
state = {
count: 0
}
render() {
return (
<div>
<button onClick={ () => {
// object.assign({}, { count: 1 }, { count: 1 }, { count: 1 }) // { count: 1}
this.setState({
count: this.state.count + 1
}, () => {
console.log('1', this.state.count) // 1
})
this.setState({
count: this.state.count + 1
},() => {
console.log('2', this.state.count) // 1
})
this.setState({
count: this.state.count + 1
},() => {
console.log('3', this.state.count) // 1
})
}}>加1</button>
{ this.state.count }
</div>
)
}
}
这种形式的
setState()
是异步的,并且在同一周期内会对多个setState
进行批处理,相当于Object.assign( prevState, {count: this.state.count + 1}, {count: this.state.count + 1}, ... )
后调用的
setState()
将覆盖同一周期内先调用setState
的值,因此商品数仅增加一次。如果后续状态取决于当前状态,建议使用 updater 函数的形式代替(前面案例已经实现)。或者在第二个参数中再继续操作。
思考题: