react的state和useState你了解多少?带你深入react state useState

文章详细解析了React中的state概念,强调了state在组件渲染中的重要性,以及useState钩子的基本用法和set函数如何触发重渲染。通过多个示例,特别是对state快照的解释,说明了在事件处理函数中多次调用set函数时state更新的机制。此外,还介绍了如何通过更新函数来正确地进行多次state更新,确保每次增加正确的值。
摘要由CSDN通过智能技术生成

state和useState是react中很重要的概念,虽然笔者一直在用,但是总感觉有些地方认识不够透彻。于是乎,笔者重新阅读学习了react官方文档,感觉受益匪浅。希望能用尽量通俗简洁的语言把吸收的知识表述清楚,便写下此文。

如有错误,欢迎指正~

目录

state

普通变量

深入了解state

useState

基本用法

set函数触发重新渲染

*state--快照(重要!)

 例一

例二 

例三

例四:更新函数 

例五

总结


state

普通变量

当我们需要数据响应式渲染到页面上时,我们就不能使用普通的变量了:

  1. 普通变量无法在多次渲染中持久保存:当 React 再次渲染这个组件时,它会从头开始渲染——不会考虑之前对局部变量的任何更改。
  2. 更改局部变量不会触发渲染。 React 没有意识到它需要使用新数据再次渲染组件。

此时,我们就需要useState来声明state去解决上面的问题。

深入了解state

官网中说到:“State是隔离私有的”,那么这句话应该如何理解呢?

隔离:State 是屏幕上组件实例内部的状态。换句话说,如果你渲染同一个组件两次,每个副本都会有完全隔离的 state!改变其中一个不会影响另一个。

私有:与 props 不同,state 完全私有于<声明>它的组件。父组件无法更改它。

//隔离:son组件渲染两次,两个son组件中的state互相隔离,互不影响
//私有:parent不知道son组件中的state的任何信息,无法改变son组件的state(符合react的单项数据流动特性)
<parent>
    <son/>
    <son/>
</parent>

useState

基本用法

useState 返回一个由两个值组成的数组:

当前的 state:在首次渲染时,它将与你传递的 initialState 相匹配。
set 函数:它可以让你将 state 更新为不同的值并《触发重新渲染》。

const [state, setState] = useState(initialState);

set函数触发重新渲染

在react中,触发组件渲染有两种情况:

  1. 组件的 初次渲染:调用render函数
  2. 组件(或者其祖先之一)的 状态发生了改变:调用set函数

在使用set函数之后,react会从该state声明(useState)的组件开始,根据更改后的state,重新渲染该组件&所有子组件。

*state--快照(重要!)

为了更好的理解,我们可以把state理解为一张快照:当 React 调用你的组件时,它会为特定的那一次渲染提供一张 state 快照。你的组件会在其 JSX 中返回一张包含一整套新的 props 和事件处理函数的 UI 快照 ,其中所有的值都是 根据那一次渲染中 state 的值 被计算出来的。

为了更好的理解,我们接下来看几个例子

 例一

该例子是一个简易计数器,click事件里面调用了三次setNumber,当我们点击按钮之后会发生什么呢?

//App.js
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}>+3</button>
    </>
  )
}

点击之后发现,每次点击值只增加1,而非3。这是为什么呢?

我们来捋一下:

step1:该组件第一次渲染时,number的值是0,所以后续的渲染都是根据0这个值进行的计算。

step2:第一次组件渲染完成后,我们看下的onClick函数:

<button onClick={() => {
  setNumber(number + 1);
  setNumber(number + 1);
  setNumber(number + 1);
}}>+3</button>

以下是这个按钮的点击事件处理函数通知 React 要做的事情:

  1. setNumber(number + 1)number 是 0 所以 setNumber(0 + 1)
    • React 准备在下一次渲染时将 number 更改为 1
  2. setNumber(number + 1)number 是0 所以 setNumber(0 + 1)
    • React 准备在下一次渲染时将 number 更改为 1
  3. setNumber(number + 1)number 是0 所以 setNumber(0 + 1)
    • React 准备在下一次渲染时将 number 更改为 1

尽管你调用了三次 setNumber(number + 1),但在 这次渲染的 事件处理函数中 number 会一直是 0,所以你会三次将 state 设置成 1。这就是为什么在你的事件处理函数执行完以后,React 重新渲染的组件中的 number 等于 1 而不是 3

所以我们在第一次渲染之后,onClick函数可以等价为如下代码: 

<button onClick={() => {
  setNumber(0 + 1);
  setNumber(0 + 1);
  setNumber(0 + 1);
}}>+3</button>

例二 

我们把上面的例子改一下,再预测一下会出现什么结果:

 

//App.js
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        alert(number);
      }}>+5</button>
    </>
  )
}

点击之后发现:数字从0变成5了,但是弹窗的数依旧是0。

根据我们上面提到的快照理解,上面的onClick代码可以等价为:

<button onClick={() => {
        setNumber(0 + 5);
        alert(0);
      }}>+5</button>

这样是不是就很好理解了。

例三

我们再改一下:如果我们加一个定时器,想让弹窗在组件重新渲染 之后 才触发,又会怎样呢?

//App.js
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setTimeout(() => {
          alert(number);
        }, 3000);
      }}>+5</button>
    </>
  )

结果: 数字从0变成5了,但是三秒之后的弹窗的数依旧是0。

这是为什么呢?我们看react官方如何解释:

一个 state 变量的值永远不会在一次渲染的内部发生变化, 即使其事件处理函数的代码是异步的。”

React 会使 state 的值始终”固定“在一次渲染的各个事件处理函数内部。

state作为快照,它的值在 React 通过调用你的组件“获取 UI 的快照”时就被“固定”了,哪怕是异步代码,它得到的state值也是在调用时被固定的快照的值。

所以对应函数等价于如下代码:

setNumber(0 + 5);
setTimeout(() => {
  alert(0);
}, 3000);

例四:更新函数 

让我们回到例一,如果我们想在onClick里面写三个set函数来实现加3的效果,那该如何改造呢?

这时候就需要用到更新函数了:在set函数中,传入一个根据队列中的前一个 state 计算下一个 state 的 函数。

简单来说,就是把set函数的参数,从之前的一个值/变量,换成一个函数即可。

更改后的代码见下图:

 这样,每点击一次,加的就是3了。

详细更新过程见下图:

例五

我们再升级一下例4的代码,运行结果会怎样呢?

结果:点击按钮后,从0变成了42。 

详细更新过程见下图:

 

总结

  • 调用set函数会请求一次新的渲染
  • 每个渲染(以及其中的函数)始终“看到”的是 React 提供给这个 渲染的 state 快照
  • 过去创建的事件处理函数拥有的是创建它们的那次渲染中的 state 值。
  • React 会等到事件处理函数中的 所有 代码都运行完毕再处理你的 state 更新。
  • 要在一个事件中多次更新某些 state,你可以使用 setNumber(n => n + 1) 更新函数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值