useState使用规则

本文详细解释了ReactHooks中的useState函数,包括其两种参数形式的应用,状态的读取和修改方法,以及函数组件的执行过程。强调了在不同情况下的最佳实践,特别是关于状态初始化和条件判断下的注意事项。
摘要由CSDN通过智能技术生成


1.为函数组件提供状态

const TestHook = () => {
  // 1.使用 useState 创建一个新的状态,返回值是一个数组,包含了两个元素
  // - 第一个元素:状态
  // - 第二个元素:更新状态的方法
  // const countState = useState(0)

  // 第一个元素:就是状态
  // const count = countState[0]

  // 第二个元素:用来更新状态的函数,相当于类组件中的 this.setState()
  // const setCount = countState[1]
        
  // 用结构语法简化写法
  const [count, setCount] = useState(0)

  const handleClick = () => {
    // 2.使用状态更新函数,进行状态的更新操作
    setCount(count + 1)
  }

  return (
    <div>
      <h1>计数:{count}</h1>
      <button onClick={handleClick}>累加</button>
    </div>
  )
}

useState 的参数可以有两种形式:

  1. useState(普通数据):比如 useState(0) / useState('abc')
  2. useState(回调函数): 比如 useState(() => { return 123 })

如果参数为回调函数:

  • 该回调函数的返回值就是创建的状态的初始值
  • 该回调函数在整个组件生命周期中只执行一次
import { useState } from "react"

const TestState = () => {
  // 当我们明确知道初始值是什么值的时候,用以下形式
  const [count, setCount] = useState(222)

  // 当我们需要经过计算,得出一个不固定的初始值的时候,用一下形式
  const [msg, setMsg] = useState(() => {
    const txt = '初始消息1:' + Date.now()
    console.log('>>>>>', txt);
    return txt
  })

  // 不要写成下面的形式,这种写法每次组件渲染都会执行,浪费性能
  //  const [message, setMessage] = useState('初始消息2' + Date.now()) 等价于下面的代码
  const txt = '初始消息2:' + Date.now()
  console.log('======', txt);
  const [message, setMessage] = useState(txt)

  return (
    <div>
      <h1>{count}|{msg}|{message}</h1>
      <button onClick={() => {
        setMsg("更新1:" + Date.now())
        setMessage("更新2:" + Date.now())
      }}>更新</button>
    </div>
  )
}
export default TestState

那么 useState 的两种参数形式,到底应该用哪种?

  1. 如果初始状态是一个已知数据,则用 useState(普通数据)
  2. 如果初始状态是要经过一些计算才能得到的,则用 useState(回调函数)

2.状态的读取和修改:

读取状态

在函数组件中用 useState 创建的状态,其实就是一个函数内的局部变量,所以它可在组件函数内的任意位置访问。

修改状态

  • setXxx(newState) 是个函数,参数为 新的状态值

  • 一定要创建 新状态 来替换 旧状态(和类组件中的 this.setState 的原则一样)

    const [person, setPerson] = useState({
        name: '张三',
        age: 18
      })
    
    const updatePersonInfo = () => {
    // 错误方式,修改了老的状态
    // person.name = '王五'
    
    // 正确的方式,新建状态,替换老的状态
    setPerson({
      ...person,
      name: '李四'
    })
        
    return(
        <div>
            <p>{person.name}|{person.age}</p>
            <button onClick={updatePersonInfo}>更新用户信息</button>
        </div>
        )
    
  • 通过 setXxx 修改状态后,组件函数会重新执行,进行界面的重新渲染

总结

  • 修改状态时,一定要用新的状态替换旧的状态,不要直接修改旧的状态,尤其是引用类型 的状态

3.函数组件的执行过程(面试题)

初次渲染:

  1. 从头开始执行该组件函数中的逻辑
  2. 当遇到 useState 调用时,就创建状态(比如 useState(0) 创建一个初始值为 0 的状态)
  3. 使用当前的状态值,进行界面渲染(此时的状态值为 0)

第二次及以后的渲染:

  1. 调用 setXXX 函数更新状态(比如 setCount(count + 1)),此时会触发组件重新渲染
  2. 重新渲染时,会再次执行组件函数中的所有代码逻辑
  3. 当再次遇到 useState 调用时,不会创建新状态,而是取之前已有状态值(比如之前更新的 1)
  4. 使用当前的状态值,进行界面渲染(此时的状态值为 1)

总结:

useState 并不是每次都创建新的状态

  • 第一次渲染才创建
  • 后续则是取之前的状态
    • React框架内部会收集每一useState()调用后生成或更新的状态到一个链表(类数组,在内存中不连续)中,以供组件更新时获取之前已有状态值。相当于函数组件外部放了一个存放状态的地方
    • 函数执行完之后,会释放掉内部所有的东西,不会被保存下来。

4.使用说明和注意事项

useState 的注意事项

  • 不能在 函数组件自定义 Hooks 函数 以外的地方调用!

  • 不能在 含有条件判断、循环遍历的代码块中调用(如 if、for、while 等)!
    在这里插入图片描述

    【原因说明】

    React Hooks 是按照组件第一次渲染时的调用顺序来存储状态的,在后续渲染中,会按这个固定顺序来操作状态。

    所以,一旦用了 if/for 等语句,就可能会导致顺序对应不上的情况发生。

    import {useState} from 'react'
    
    //相当于在函数组件的 外面 放了一个存放状态的地方
    const arr = [message,count,message2]  // 链表(类数组,元素在内存中不连续)。单项链表,下一个元素知道上一个元素的位置;双向链表,每个元素都知道上一个和下一个元素的位置。
    
    const TestComp = () => {
     const [message,setMessage] = useState('init value') 
    
     if(true){
     // 如果函数组件第一次渲染时,若判断条件为true,链表中就会在第二个位置存放 cuont 。
     //当组件第二次渲染时,若判断条件为false,会导致if语句后面的状态 message2 按存放顺序从链表中取出 count 的值。
    
         const [count,setCount] = useState(0)
     }
    
     const [message2,setMessage2] = useState('init value')
    }
    

通过 React 开发者工具,查看组件中的状态和顺序:

在这里插入图片描述

总结

以上提到的这些规则,同样也适用于任意的 React Hooks 函数 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值