React 组件中 State 的定义、使用及正确更新方式
🌈个人主页:前端青山
🔥系列专栏:React篇
🔖人终将被年少不可得之物困其一生
依旧青山,本期给大家带来React篇专栏内容React 组件中 State 的定义、使用及正确更新方式
前言
在 React 应用开发中,state
是组件内部用来存储和管理数据的关键概念。它允许组件根据不同的状态展示不同的 UI。本文将详细介绍 state
的定义、使用方式以及如何正确地更新 state
,帮助开发者更好地理解和运用这一核心特性。
目录
前言
1.1 state及其特点
1.2 state的定义和使用
1.2.1 es6的类 - 构造函数
1.2.2 es7的类 - 属性初始化器
1.3 如何正确的修改state
1.4 this.setState()方法及其特点
1.4.1 传递函数
1.4.2 传递对象
总结
1.1 state及其特点
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件
不要直接修改state:构造函数是唯一可以给 this.state
赋值的地方。
state更新可能是异步的:出于性能考虑,React 可能会把多个 setState()
调用合并成一个调用。
state更新会被合并:当你调用 setState()
的时候,React 会把你提供的对象合并到当前的 state
1.2 state的定义和使用
目前react中的状态有两种使用方式:
1.2.1 es6的类 - 构造函数
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入时,后缀名可以省略,可以在webpack中配置
// import App from './01-App-parent-child'
// import App from './02-App-parent-child-value'
// import App from './03-App-parent-child-value-default'
// import App from './04-App-parent-child-value-default-type'
// import App from './05-App-props-children'
// import App from './06-App-mutiple-props-children'
// import App from './07-App-mouse-tracker'
// import App from './08-App-render-props'
import App from './09-App-state-es6'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />)
src/09-App-state-es6.jsx
import React, { Component } from 'react';
/**
* ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错。
这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,
得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。
如果不调用super()方法,子类就得不到自己的this对象。
ES5 的继承机制,是先创造一个独立的子类的实例对象,
然后再将父类的方法添加到这个对象上面,即“实例在前,继承在后”。
ES6 的继承机制,则是先将父类的属性和方法,加到一个空的对象上面,
然后再将该对象作为子类的实例,即“继承在前,实例在后”
*/
class App extends Component {
// es6的类 - 构造函数
constructor (props) {
super(props) // 调用父类的constructor(props)
this.state = { // 添加子类自己的实例属性和方法,在react中 state作为初始化状态的属性
date: new Date()
}
}
render() {
return (
<div>
现在的时间是:{ this.state.date.toLocaleDateString() + this.state.date.toLocaleTimeString() }
</div>
);
}
}
export default App;
1.2.2 es7的类 - 属性初始化器
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入时,后缀名可以省略,可以在webpack中配置
// import App from './01-App-parent-child'
// import App from './02-App-parent-child-value'
// import App from './03-App-parent-child-value-default'
// import App from './04-App-parent-child-value-default-type'
// import App from './05-App-props-children'
// import App from './06-App-mutiple-props-children'
// import App from './07-App-mouse-tracker'
// import App from './08-App-render-props'
// import App from './09-App-state-es6'
import App from './10-App-state-es7'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />)
src/10-App-state-es7.jsx
import React, { Component } from 'react';
// 推荐写法
class App extends Component {
state = { // es7 类的属性
date: new Date()
}
render() {
return (
<div>
现在的时间是:{ this.state.date.toLocaleDateString() + this.state.date.toLocaleTimeString() }!!!
</div>
);
}
}
export default App;
1.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 的更新会被合并
1.4 this.setState()方法及其特点
setState()
会对一个组件的 state
对象安排一次更新。当 state 改变了,该组件就会重新渲染。
setState()
可以添加两个参数,
setState()
的第二个参数为可选的回调函数,它将在 setState
完成合并并重新渲染组件后执行
1.4.1 传递函数
参数一为带有形式参数的 updater
函数:
this.setState((state, props) => stateChange[, callback] )
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入时,后缀名可以省略,可以在webpack中配置
// import App from './01-App-parent-child'
// import App from './02-App-parent-child-value'
// import App from './03-App-parent-child-value-default'
// import App from './04-App-parent-child-value-default-type'
// import App from './05-App-props-children'
// import App from './06-App-mutiple-props-children'
// import App from './07-App-mouse-tracker'
// import App from './08-App-render-props'
// import App from './09-App-state-es6'
// import App from './10-App-state-es7'
import App from './11-App-setState-function'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />)
src/11-App-setState-function.jsx
import React, { Component } from 'react';
class App extends Component {
state = {
count: 100
}
render() {
return (
<div>
{ this.state.count }
<button onClick={ () => {
this.setState((state, props) => {
console.log(state, props)
return {
count: state.count + 1
}
})
this.setState((state, props) => {
console.log(state, props)
return {
count: state.count + 1
}
})
this.setState((state, props) => {
console.log(state, props)
return {
count: state.count + 1
}
})
} }>加</button>
</div>
);
}
}
export default App
updater 函数中接收的
state
和props
都保证为最新。updater 的返回值会与state
进行浅合并。
1.4.2 传递对象
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入时,后缀名可以省略,可以在webpack中配置
// import App from './01-App-parent-child'
// import App from './02-App-parent-child-value'
// import App from './03-App-parent-child-value-default'
// import App from './04-App-parent-child-value-default-type'
// import App from './05-App-props-children'
// import App from './06-App-mutiple-props-children'
// import App from './07-App-mouse-tracker'
// import App from './08-App-render-props'
// import App from './09-App-state-es6'
// import App from './10-App-state-es7'
// import App from './11-App-setState-function'
import App from './12-App-setState-object'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />)
src/12-App-setState-object.jsx
import React, { Component } from 'react';
// 为什么?
// const obj = { a: 100 }
// es6 中对象合并
// const newObj = Object.assign(obj, {a: 100 + 1}, {a: 100 + 1}, {a: 100 + 1})
// console.log(newObj) // { a: 101 }
class App extends Component {
state = {
count: 10
}
render() {
return (
<div>
{ this.state.count }
<button onClick={ () => {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
console.log(this.state.count)
} }>加</button>
</div>
);
}
}
export default App;
这种形式的
setState()
是异步的,并且在同一周期内会对多个setState
进行批处理,相当于Object.assign( prevState, {count: this.state.count + 1}, {count: this.state.count + 1}, ... )后调用的
setState()
将覆盖同一周期内先调用setState
的值,因此商品数仅增加一次。如果后续状态取决于当前状态,建议使用 updater 函数的形式代替(前面案例已经实现)。或者在第二个参数中再继续操作。
src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
// 引入时,后缀名可以省略,可以在webpack中配置
// import App from './01-App-parent-child'
// import App from './02-App-parent-child-value'
// import App from './03-App-parent-child-value-default'
// import App from './04-App-parent-child-value-default-type'
// import App from './05-App-props-children'
// import App from './06-App-mutiple-props-children'
// import App from './07-App-mouse-tracker'
// import App from './08-App-render-props'
// import App from './09-App-state-es6'
// import App from './10-App-state-es7'
// import App from './11-App-setState-function'
// import App from './12-App-setState-object'
import App from './13-App-setState-callback'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />)
src/13-App-setState-callback.jsx
import React, { Component } from 'react';
class App extends Component {
state = {
count: 10
}
render() {
return (
<div>
{ this.state.count }
<button onClick={ () => {
this.setState({
count: this.state.count + 1
}, () => {
this.setState({
count: this.state.count + 1
}, () => {
this.setState({
count: this.state.count + 1
})
})
})
console.log(this.state.count) // 10
} }>加</button>
</div>
);
}
}
export default App;
思考题:
1.何时以及为什么 setState() 会批量执行?
2.为什么不直接更新 this.state?
总结
通过本文的介绍,我们了解了 state
在 React 组件中的重要性,以及如何在 ES6 和 ES7 类组件中定义和使用 state
。同时,我们还探讨了正确更新 state
的方法,包括使用 setState()
方法时需要注意的事项。遵循这些最佳实践,可以帮助我们避免常见的陷阱,提高应用的性能和可靠性。