[React]初步了解State | State的更新

State的实现

我们知道,在 React 中,组件内部某些属性的值依靠对应的 state 进行储存记忆

useState 的唯一参数是 state 变量的初始值,每一次组件渲染时, useState 会返回一个数组,其中包含:

  • state 变量,会保存上次渲染的值
  • state setter 函数,用于更新 state 变量的值

在实际运用中,state 是通过这样的步骤运行的

  1. **组件进行第一次渲染。**此时传入 useState 的初始值作为 state 变量的值。
  2. **更新 state 。**当调用 state setter 函数时,会传入一个新的 state 值,React 现在记住新的值并进行下一步渲染。
  3. 组件的第二次渲染。 此时 useState 的参数依然是初始值,但 state setter 使 React 记住了新值,所以会返回 [newState, stateSetter]

关于UseState的工作原理

  • 在 React 内部,为每个组件保存了一个数组,其中每一项都是一个 state 对。
  • 它维护当前 state 对的索引值,在渲染之前将其设置为 “0”。
  • 每次调用 useState 时,React 都会为你提供一个 state 对并增加索引值

useState 调用,state初始化

function Gallery() {
  // 每次调用 useState() 都会得到新的 pair
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);
  //...
    
}    

useState 内部

let componentHooks = [];
let currentHookIndex = 0;

function useState(initialState) {
  let pair = componentHooks[currentHookIndex];
  //...
  pair = [initialState, setState]
  //...
  componentHooks[currentHookIndex] = pair;
  currentHookIndex++;
  return pair;
}

依据传入的initialState进行初始化,在componentHooks 中存储pair,并更新currentHookIndex

使用setState进行 state 的更新

let componentHooks = [];
let currentHookIndex = 0;

function useState(initialState) {
  let pair = componentHooks[currentHookIndex];
  if (pair) {
    // 非首次渲染,pair已经存在
    // 将其返回并为下一次 hook 的调用做准备
    currentHookIndex++;
    return pair;
  }

  pair = [initialState, setState];
  
  function setState(nextState){
    //将新的值放入pair
    pair[0] = nexxtState;
    //UpdateDom将currentHookIndex赋值为0,保证了每一次更新的state都在initialState序号为零的位置
    UpdateDom();
  }
  //存储pair用于之后的渲染
  //并为下一次的hook调用做准备
  componentHooks[currentHookIndex] = pair;
  currentHookIndex++;
  return pair;
}

DOM的渲染

function updateDOM() {
  // 在渲染组件之前
  // 重置当前 Hook 的下标
  currentHookIndex = 0;

  // 更新 DOM 的具体代码省略
  //...

如果一个组件进行两次渲染,那么每个组件副本都会有独立的 state

渲染与提交

渲染

组件被 React 渲染之后,显示到页面中。组件的渲染包括三个步骤:

  1. 触发 一次渲染
    • 组件的 初次渲染。
    • 组件(或者其祖先之一)的 状态发生了改变。
  2. 渲染 组件
    • 在进行初次渲染时, React 会调用根组件。
    • 对于后续的渲染, React 会调用内部状态更新触发了渲染的函数组件。
  3. 提交 到 DOM
    • 对于初次渲染, React 会创建的所有 DOM 节点放在屏幕上。
    • 对于重渲染, React 将应用最少的必要操作,以使得 DOM 与最新的渲染输出相互匹配。

渲染与 state

  • 设置 state 会触发渲染,而渲染会产生一张快照。
  • 在一次渲染中,state 不会发生改变,React 会使 state 的值始终”固定“在一次渲染的各个事件处理函数内部。
  • state 改变是通过重新渲染实现的

因此,对于重复的setState

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>
    </>
  )
}

此次渲染的number都是同一个值,多次调用setNumber 不会使number+3

setNumber 事实上是告知了React:我下一次渲染的时候,这个number要加一。

而多次的setNumber ,仅仅只是告知了三次number要加一,而非number要加三

将多次更新加入队列

setState(x) 实际上会像 setState(n => x) 一样运行

setState的参数实际感觉起来更像是一个回调

因此如果使用这样的方法,我们可以实现将一系列更新通过一次渲染实现:

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

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

React 是这样处理的:

  1. setNumber(n => n + 1)n => n + 1 是一个函数。React 将它加入队列。
  2. setNumber(n => n + 1)n => n + 1 是一个函数。React 将它加入队列。
  3. setNumber(n => n + 1)n => n + 1 是一个函数。React 将它加入队列。
更新队列n返回值
n => n + 100 + 1 = 1
n => n + 111 + 1 = 2
n => n + 122 + 1 = 3

总结:

我们可以将两种内容传递给 setNumber state

  • 一个更新函数(例如:n => n + 1)会被添加到队列中。
  • 任何其他的值(例如:数字 5)会导致“替换为 5”被添加到队列中,已经在队列中的内容会被忽略。
  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值