react setState

因为 setStatereact中非常重要,所以单独拎出来整理它的要点

什么是setState?

react是通过在constructor构造函数中的this.state去定义状态的,然后是通过this.setState去修改状态,那么setState有什么特点呢?

不可变值

state中数据不可直接使用this.state.xxx = xxx形式来改变状态,这是因为reactimmutable概念决定的

  • 针对基础类型
    直接修改this.state值类型时,会报警告:Do not mutate state directly. Use setState()
 this.setState({
      // 使用this.state.count++,会报警告,因为这句直接修改了原count的值:
      // Do not mutate state directly. Use setState()
      // count: this.state.count++
      count: this.state.count + 1,
    })
  • 针对数组
    虽然使用this.state.list.push(xx)这种方式修改数组后,再使用setState赋给state不会报错,但还是不建议这么使用,我们应该遵循immutable理念,通过拷贝后的数组,再赋值给state
// 建议使用这种,对原数据进行拷贝,在拷贝上修改
    const newList = this.state.list.slice()
    newList.push(3)
    this.setState({
      list: newList
    })
  • 针对对象
    使用Object.assign,或者使用扩展运算符生成一个新对象方式
this.setState({
  obj1: Object.assign({}, this.state.obj1, {x: 1}),
  obj2: {...this.state.obj2, x: 1}
})
import React, { Component } from 'react'

class SetStateDemo extends Component{
  constructor(props) {
    super(props)
    this.state = {
      count: 0,
      list: [
        {id: 1, age: 1},
        {id: 2, age: 2}
      ],
      obj1: {
        a: 1
      }
    }
  }
  addCount = () => {
    // 建议使用这种,对原数据进行拷贝,在拷贝上修改
    const newList = this.state.list.slice()
    newList.push(3)
    this.setState({
      // 使用this.state.count++,会报警告,因为这句直接修改了原count的值:
      // Do not mutate state directly. Use setState()
      // count: this.state.count++
      count: this.state.count + 1,
      list: newList,
      obj1: {...this.state.obj1, x: 1}
    })
    console.log(this.state.count) // 这里是异步的
  }
  render() {
    return (
      

{this.state.count}

) } } export default SetStateDemo;

setState是同步的吗

setState有时是同步的,有时是异步的,不能一概而论,要区分使用情况来分析:

异步的setState:

直接使用onXxxx绑定的事件,使用的setState是异步的,如果要实时获取数据,需要在setState第二个参数回调函数中获取(类似vue中的nextTick作用)

  constructor(props) {
    super(props)
    this.state = {
      count: 0,
      list: [
        {id: 1, age: 1},
        {id: 2, age: 2}
      ]
    }
  }
  addCount = () => {
    this.setState({
      count: this.state.count + 1,
    }, () => {
      console.log('在回调中获取异步后的结果:', this.state.count)
    })
    console.log(this.state.count) // 这里是异步的
  }
image.png

同步的setState

  1. setTimeout中是同步的
render() {
    return (
      
) } addCount2 = () => { setTimeout(() => { this.setState({ count: this.state.count + 1 }) console.log('setTimeout中的setState:', this.state.count) }) }
image.png
  1. 在自定义DOM事件中,是同步的
    componentDidMount生命周期中增加自定义事件,发现执行的setState是同步的
    注意:自定义事件要及时在componentWillUnmount生命周期解绑,不然会造成内存泄露
class SetStateDemo extends Component{
  constructor(props) {
    super(props)
    this.state = {
      count: 0,
    }
  }
  componentDidMount() {
    // 自定义事件
    document.body.addEventListener('click', this.addCount3)
  }
  addCount3 = () => {
    this.setState({
      count: this.state.count + 1
    })
    console.log('自定义事件中的setState:', this.state.count)
  }
  componentWillUnmount() {
    // 自定义事件要及时解绑,否则会造成内存泄露
    document.body.removeEventListener('click', this.addCount3)
  }
  render() {
    return (
      

{this.state.count}

) }
image.png

setState是否会被合并

  1. 当使用对象来修改状态时,会被合并
    因为在merge方法中,this.setState是异步执行的,当执行完this.setState({ count: this.state.count + 1 })时,this.state.count还没有更新;
    所以执行下一个this.setState时,this.state.count还是原来的0,所以执行完三次后,相当于执行三次this.setState({ count: 1 })
    当异步更新时,这三次结果被合并了,类似执行Object.assign({count: 1}, {count: 1}, {count: 1}),结果为{count: 1},所以点击一次this.state.count结果是每次加1
render() {
    return (
      
) } merge = () => { // 使用对象时,结果会被合并 this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) }
image.png
  1. 使用函数时,结果则不会被合并
    使用函数时,会默认有两个参数:

    • preState: 为当前组件的state
    • props: 为父组件传递的属性

    通过preState修改state,然后return返回一个对象,跟我们平常使用setState的对象格式是一样的

  render() {
    return (
      
) } merge = () => { // 使用函数,则不会被合并,执行结果+3 this.setState((preState, props) => { return { count: preState.count + 1 } }) this.setState((preState, props) => { return { count: preState.count + 1 } }) this.setState((preState, props) => { return { count: preState.count + 1 } }) console.log('使用函数:', this.state.count) }
image.png

可以看到,无论是用对象,还是用函数,setState是异步返回结果是不会变的

你可能感兴趣的:(react setState)