react - State

六、State

stateclass组件的内置对象,用于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 函数中接收的 stateprops 都保证为最新。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 函数的形式代替(前面案例已经实现)。或者在第二个参数中再继续操作。

思考题:

1.何时以及为什么 setState() 会批量执行?

2.为什么不直接更新 this.state

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值