在 React 应用开发中,state 是组件内部用来存储和管理数据的关键概念。它允许组件根据不同的状态展示不同的 UI。本文将详细介绍 state 的定义、使用方式以及如何正确地更新 state,帮助开发者更好地理解和运用这一核心特性。
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件
不要直接修改state:构造函数是唯一可以给 this.state 赋值的地方。
state更新可能是异步的:出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。
state更新会被合并:当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state
目前react中的状态有两种使用方式:
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;
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;
setState() 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式.
将 setState() 视为请求而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。
setState() 并不总是立即更新组件。它会批量推迟更新。这使得在调用 setState() 后立即读取 this.state 成为了隐患。为了消除隐患,请使用 componentDidUpdate 或者 setState 的回调函数(setState(updater, callback)),这两种方式都可以保证在应用更新后触发。
记住修改状态的三大原则:
- state = { a: 10 }
- this.state.a = 100 // ❌
- 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
setState() 会对一个组件的 state 对象安排一次更新。当 state 改变了,该组件就会重新渲染。
setState()可以添加两个参数,
setState() 的第二个参数为可选的回调函数,它将在 setState 完成合并并重新渲染组件后执行
参数一为带有形式参数的 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 进行浅合并。
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;
思考题:
通过本文的介绍,我们了解了 state 在 React 组件中的重要性,以及如何在 ES6 和 ES7 类组件中定义和使用 state。同时,我们还探讨了正确更新 state 的方法,包括使用 setState() 方法时需要注意的事项。遵循这些最佳实践,可以帮助我们避免常见的陷阱,提高应用的性能和可靠性。