学习React中的State声明式

132 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第34天,点击查看活动详情

1. 如何定义state?

  在类组件中,在constructor()中使用this.state={} 来定义:
  在函数式中,自React16.8以后可以使用 useState()来定义

2. 如何使用state?

 在类组件中,通过this.state来访问声明式变量;
 在函数式组件中,直接访问useState的结果

3. 如何正确的修改state声明式变量?

 在类组件中,使用专属方法this.setState()来修改;
 在函数式组件中,使用useState()返回值的第二个参数来修改。

4. 详解 this.setState()的两种写法

 this.setState({ },callback)  当我们修改state时,如果新值与旧值无关,建议使用这种写法
 this.setState((state,props)=>{ },callback) 当我们修改state时,如果新值与旧值有关,建议使用这种写法

5. 详解 this.setState()的异步性

React(V17/V16)中,this.setState()在合成事件(on*系列事件、生命周期)中,是异步的;在宏任务、Promise.then()中是同步的
 在React(V18)中,无论this.setState()在哪里。都是异步的。这种特性,被称之为“并发模式”
 为什么是要把this.setState()这个语句定义成异步的呢?为了性能优化

6. 详解 this.setState()的自动合并

什么是this.setState()的自动合并呢?在同一个函数域中,多个this.setState()会自动合并,以减少没有必要的Diff运算(协调运算)
自动合并的规则:是一种浅合并

7. 事件绑定

 在类组件中,使用 <div onEventName={this.handler.bind(this,10)} />,
 还可以使用 <div onEventName={()=>this.handler(10)} />
 在函数式组件中,只能使用箭头函数的方式绑定
import { Component,useState } from 'react'

class A extends Component{
  constructor(props){
    super(props)  // 调用 Component的构造函数
    // 定义 state
    this.state={
      num:10,
      foo:100,
      name:'张三',
      bar:200,
      cate:'',
      bol:true,
      list:[]
    }
  }

  // 自定义方法(事件处理器)
  add(){
    console.log('---1run---',this.state.num)
    // 【错!!】为什么这段代码不好?因为你直接修改了state,又使用this.setState()间接修改state
    // this.setState({
    //   num:++this.state.num
    // })

    // 【对!不优雅】这种写法是没有问题的,但是不够优雅。因为你要修改的state新值是由旧值计算而来的
    // 当state新值由旧值计算而来时,建议使用 this.setState((state,props)=>{ })这种语法
    // this.setState({
    //   num:this.state.num+1
    // })

    // 【对!!】
    // this.setState((state,props)=>{
    //   return{
    //     num:state.num+1   // 用旧值参与运算,得到新值
    //   }
    // })

    // 【对!!】
    this.setState(state=>({num:state.num+1}))
    console.log('---2 num --',this.state.num)
  }

  sub(){
    // 如果这段代码是写在V17中,this.setState()是同步的,打印结果是100 - 98 - 96 - 94 -...
    // 如果这段代码是卸载V18中,this.setState()仍然是异步的,打印结果是 100 - 100 - 96 -96 -...
    setTimeout(()=>{
      console.log('1--foo',this.state.foo)
      this.setState(state=>({foo:state.foo-2}),()=>console.log('3---foo',this.state.foo))
      console.log('2--foo',this.state.foo)
    },0)
  }

  update(){
    // do something
    this.setState({name:'李四'})
    // do something
    this.setState(state=>({bar:state.bar-10}))
    // do something
    this.setState({ name:'李四'})
    // do something
    this.setState(state=>({
      name:'赵六',
      bar:state.bar+20
    }))
  }

  // 一定要有render(),因为render()返回值是视图结构
  render(){
    // do something
    const {num,foo,name,bar}=this.state

    console.log('--remder--')

    return (
      <div>
        <h1>类组件</h1>
        <h1>{this.state.num}</h1>
        {/*使用ES5的方式,绑定事件*/}
        <button onClick={this.add.bind(this)}>自增[ES5]</button>
        {/*使用ES6的方式,绑定事件*/}
        <button onClick={()=>this.add()}>自增[ES6]</button>

        <hr />

        <h1>{ foo }</h1>
        <button onClick={()=>this.sub()}>自减</button>

        <hr />

        <h1>{ name }</h1>
        <h1>{ bar }</h1>
        <button onClick={()=>this.update()}>更新</button>
      </div>
    )
  }
}

function B(props) {
  let [num,setNum]=useState(1)
  const [list,setList]=useState([])

  const add=()=>{
      console.log('1---num',num)
      // 如果这里是React(V17)中,在合成事件中是异步的,如果是在宏任务或者Promise.then()是同步的。
      // 如果这里是React(V18)中。无论在哪里都是异步的,
      // useState()给的这种set*方法,是没有callback的
      // setNum(num+1)
      setNum(num=>num+10)
      console.log('2---num',num)
  }

  return (
    <div>
      <h1>函数式组件</h1>
      <h1>{num}</h1>
      <button onClick={add}>自增</button>
    </div>
  )
}

export default B