前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开篇:通过 state 阐述 React 渲染

开篇:通过 state 阐述 React 渲染

作者头像
奋飛
发布2024-05-25 19:18:50
690
发布2024-05-25 19:18:50
举报
文章被收录于专栏:Super 前端

前段时间开始着手React项目的开发,关于React的一些思想也有了一些体会(尤其是同vue之间的差异),特梳理&总结相关内容,便于理解。

✓ 🇨🇳 开篇:通过 state 阐述 React 渲染

说在前面

React中,有两种原因会导致组件的渲染:

  1. 组件的 初次渲染。
  2. 组件(或者其祖先之一)的 状态发生了改变。

State setter 函数更新变量(状态发生改变)并触发 React 再次渲染组件。

useState Hook 提供了这两个功能:

  1. State 变量 用于保存渲染间的数据。
  2. State setter 函数 更新变量并触发 React 再次渲染组件。
核心要点

「React 组件显示到屏幕,包括三个步骤:」

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

通过 setInterval 实现每秒+1

代码语言:javascript
复制
import React, { useState, useEffect } from "react";

export default () => {
  // 定义计数
	const [count, setCount] = useState(0);
  
  /* 需求:实现每1秒+1 */
  useEffect(() => {
    const interval = setInterval(() => setCount(count + 1), 1000)
    return () => clearInterval(interval)
  }, [])
  
 return (
   <>
   		<p>count: {count}</p>
   </>);
}  

上述无法实现想要的效果!setInterval 函数每隔1秒执行一次,但 count 结果一直是1。

以下是 setInterval 函数通知 React 要做的事情:

前提:useEffect(() => {}, []) 1只执行一次,不会在组件任何的 props 或 state 发生改变时重新运行。

在第一次渲染期间,count0

  1. setCount(count + 1)count0 所以 setCount(0 + 1)
    • React 准备在下一次渲染时将 count 更改为 1
  2. setCount(count + 1)count0 所以 setCount(0 + 1)
    • React 准备在下一次渲染时将 count 更改为 1
  3. 每隔1秒,执行一次上述操作

尽管每1秒调用一次 setNumber(count + 1),但在 这次渲染count 一直是 0,每1秒将 state 设置成 1

一个 state 变量的值永远不会在一次渲染的内部发生变化, 即使其事件处理函数的代码是异步的。它的值在 React 通过调用组件“获取 UI 的快照”时就被“固定”了。

React 执行函数 => 计算快照 => 更新 DOM 树

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

下述例子,更容易说明上述「快照」的含义。点击一次按钮,alert 弹出 0 而不是 5

代码语言:javascript
复制
<button onClick={() => {
  setNumber(number + 5);
  setTimeout(() => {
    alert(number);
  }, 3000);
}}>+5</button>

结合上述问题,下述提供一些方案 >>>

给 useEeffect 添加响应依赖

😹性能较差,每次setInterval都会被销毁&重建(导致 Effect 在每次 count 更改时再次执行 cleanup 和 setup)

代码语言:javascript
复制
useEffect(() => {
	const interval = setInterval(() => setCount(count + 1), 1000)
  return () => clearInterval(interval)
}, [count])
通过更新函数设置 state 值

👍函数式更新,该函数将接收先前的 state ,并返回一个更新后的值。这样定时器每次拿到的是最新的值

代码语言:javascript
复制
useEffect(() => {
	const interval = setInterval(() => setCount(v => v + 1), 1000)
	return () => clearInterval(interval)
}, [])

React 将更新函数放入 队列 中。然后,在下一次渲染期间,它将按照相同的顺序调用它们:

  1. v => v + 1 将接收 0 作为待定状态,并返回 1 作为下一个状态。
  2. v => v + 1 将接收 1 作为待定状态,并返回 2 作为下一个状态。
  3. 实现每1秒加1…

延伸:

代码语言:javascript
复制
<button onClick={() => {
  setNumber(number + 5);
  setNumber(n => n + 1);
}}>增加数字</button>
  1. setNumber(number + 5)number0,所以 setNumber(0 + 5)。React 将 “替换为 5 添加到其队列中。
  2. setNumber(n => n + 1)n => n + 1 是一个更新函数。 React 将 该函数 添加到其队列中。

总结:

  • 设置 state 不会更改现有渲染中的变量,但会请求一次新的渲染。
  • React 会在事件处理函数执行完成之后处理 state 更新。这被称为批处理。
  • 要在一个事件中多次更新某些 state,你可以使用 setNumber(n => n + 1) 更新函数。
借助 ref

👉useRef 返回一个可变的 ref 对象,返回的 ref 对象在组件的整个生命周期内保持不变。将定时器函数提取出来,每次定时器触发时,都能取到最新到 count

代码语言:javascript
复制
const counterRef: any = useRef(null)
counterRef.current = () => {setCount(count + 1)}
useEffect(() => {
	const interval = setInterval(() => counterRef.current(), 1000)
	return () => clearInterval(interval)
}, [])
使用 useLatest hook

👉使用返回当前最新值的 Hook(ahooks),可以避免闭包问题。

useLatest 返回的永远是最新值3

代码语言:javascript
复制
const latestCountRef = useLatest(count);
useEffect(() => {
  const interval = setInterval(() => setCount(latestCountRef.current + 1), 1000)
  return () => clearInterval(interval)
}, [])
参考链接
  1. https://react.docschina.org/learn/lifecycle-of-reactive-effects#what-an-effect-with-empty-dependencies-means 依赖项为空数组的 Effect ↩︎
  2. https://react.docschina.org/learn/state-as-a-snapshot state 如同一张快照 ↩︎
  3. https://ahooks.js.org/zh-CN/hooks/use-latest ahooks useLatest ↩︎
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 说在前面
  • 核心要点
  • 示例
    • 给 useEeffect 添加响应依赖
      • 通过更新函数设置 state 值
        • 借助 ref
          • 使用 useLatest hook
          • 参考链接
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档