当前位置: 首页 > news >正文

ReactHooks:渲染与useState

渲染和提交

组件显示到屏幕之前,必须被 React 渲染。主要需要经历以下三个步骤:

步骤1: 触发一次渲染

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

  • 组件的初次渲染
  • 组件(或其父组件)的状态发生改变而触发重新渲染

当应用启动时,会触发初次渲染。一旦组件被初次渲染,就可以调用 set 函数(useState)更新其状态来触发之后的渲染。更新组件的状态会自动将一次渲染送入队列。

可以把这种情况想象成餐厅客人在第一次下单之后又点了茶、点心和其他东西,每次点单到厨房都相当于触发一次渲染。

步骤2:React 渲染组件

在触发一次渲染后,React 会调用组件来确定在屏幕上显示的内容。

这个过程是递归的,如果更新后的组件会返回某个另外的组件(子组件),React 会继续渲染这个子组件。以此类推,这个过程会持续下去,直到没有更多的嵌套组件并且 React 确切知道哪些东西应该显示到屏幕上为止。

如果更新的组件在树中的位置非常高,默认会渲染更新组件中所有的嵌套组件,这种行为可能会影响性能。

在渲染组件的过程中,React 会将更新后的虚拟 DOM 与旧的虚拟 DOM 进行对比,通过 diff 算法找出需要更新的节点,从而在提交阶段应用最少的必要操作来操作真实 DOM,提交性能。

  • 真实DOM:浏览器在解析HTML文档时,会将每个标签元素抽象成DOM节点,按照标签元素层次分明的结构,将HTML文档构建成一棵DOM树。
  • 虚拟DOM:虚拟DOM本质上是一个js对象,通过对象来表示真实的DOM结构。tag用来描述标签,props用来描述属性,children用来表示嵌套的层级关系。在状态变化时,会重新生成一个新的虚拟DOM,通过将新旧两个虚拟DOM树进行比较,只将差异的节点应用在真实DOM树上。

步骤3:React 把更新提交到 DOM 上

在渲染组件之后,React 仅在渲染之间存在差异时才会更改 DOM 节点。在渲染完成并且 React 更新 DOM 之后,浏览器就会重新绘制屏幕。

state

想要页面对输入做出反应,需要设置 state 触发重新渲染。

state 如同一张快照,React 在调用(渲染)组件时,组件会在其 JSX 中返回一张包含一整套新的 props 和事件处理函数的 UI 快照,其中所有的值都是根据这一次渲染中 state 中的值计算出来的。

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

const [count, setCount] = useState(0);const onClick = () => {setCount(count + 1);setCount(count + 1);setTimeout(() => {alert(count);}, 3000);
}
// 最终结果:点击按钮之后,页面触发重新渲染 count会变为 1,3秒后 alert显示内容为 0

在这次渲染的过程中,state 变量 count 的值为 0,使用替代法可以将事件处理函数变成下面这样:

setCount(0 + 1);
setCount(0 + 1);
setTimeout(() => { alert(0) }, 3000);

除此之外,state 变量还有一个重要的特性:批处理

React 会将一系列的 state 更新加入队列,等到事件处理函数中的所有代码都运行之后再遍历队列,进行 state 更新。这使得可以一次更新多个 state 变量而不会触发太多次的重新渲染。

const [count, setCount] = useState(0);

setCount(count + 1);
setCount(count + 1);			// 1
// setCount(0 + 1);
// setCount(0 + 1);
setCount(count + 1);
setCount(count + 5);			// 5
// setCount(0 + 1);
// setCount(0 + 5);
setCount(count => count + 1);
setCount(count => count + 1);	// 2
// setCount(0 => 0 + 1);
// setCount(1 => 1 + 1);
setCount(count => count + 1);
setCount(count + 5);			// 5
// setCount(0 => 0 + 1);
// setCount(0 + 5);
setCount(count + 1);
setCount(count => count + 5);	// 6
// setCount(0 + 1);
// setCount(1 => 1 + 5);

state 更新

state 中可以存储任意类型的 js 值。对于基础数据类型,可以通过直接替换它们的值来触发一次渲染。对于对象或数组,则需要创建一个新的对象或数组并将其传递给 state 的设置函数。

对象:使用 … 展开语法进行复制

const [person, setPerson] = useState({name: 'Niki de Saint Phalle',artwork: {title: 'Blue Nana',city: 'Hamburg',image: 'https://i.imgur.com/Sd1AgUOm.jpg',}
});setPerson({...person, 	// 复制其它字段的数据 artwork: { 	// 替换 artwork 字段 ...person.artwork, 	// 复制之前 person.artwork 中的数据city: 'New Delhi' 	// 但是将 city 的值替换为 New Delhi!}
});

数组

const [ products, setProducts ] = useState([{ name: 'Baklava', count: 1},{ name: 'Cheese', count: 5},
])

新增:… 数组展开

setProducts([ ...products, { name: 'Spaghetti', count: 2}
]);	 // 可以加在开头或末尾

插入中间:slice

setProducts([...products.slice(0, 1),{ name: 'Spaghetti', count: 2},...products.slice(1),
])

删除:filter

setProducts(products.filter(p => p.count <= 1);
)

更新:map

setProducts(products.map(p => {if (p.name === 'Cheese'){return { ...p, count: p.count + 1};}return p;})
)

排序:先拷贝一份

const newProducts = [ ...products];
newProducts.sort((a, b) => b.count - a.count);
setProducts(newProducts);

相关文章:

  • openssl3.2 - 在VS2019下源码调试openssl.exe
  • 如何通过Burp Suite专业版构建CSRF PoC
  • 【纯CSS特效源码】(二)精美的立体字
  • JavaScript基础05
  • css 怎么绘制一个带圆角的渐变色的边框
  • UGUI Image图像控件替换图片
  • Java 8 中的 Stream 轻松遍历树形结构
  • 视频转为序列图的软件,让视频批量转为序列图
  • Object.keys()
  • Go语言学习记录——用正则表达式(regexp包)来校验参数
  • mysql进阶-索引基础
  • 高效构建Java应用:Maven入门和进阶(五)
  • 【JavaScript】es6开发常用技巧
  • Page 251~254 Win32 GUI项目
  • 使用MATLAB连接USRP
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • AngularJS指令开发(1)——参数详解
  • GraphQL学习过程应该是这样的
  • HTTP中的ETag在移动客户端的应用
  • iOS 系统授权开发
  • JavaScript类型识别
  • SAP云平台里Global Account和Sub Account的关系
  • 动态规划入门(以爬楼梯为例)
  • 浮现式设计
  • 扑朔迷离的属性和特性【彻底弄清】
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 思考 CSS 架构
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 移动端唤起键盘时取消position:fixed定位
  • 在Mac OS X上安装 Ruby运行环境
  • 自定义函数
  • ​【已解决】npm install​卡主不动的情况
  • (06)Hive——正则表达式
  • (2020)Java后端开发----(面试题和笔试题)
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (floyd+补集) poj 3275
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (二)fiber的基本认识
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (算法)前K大的和
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • .Net MVC4 上传大文件,并保存表单
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .net 验证控件和javaScript的冲突问题
  • .NET的微型Web框架 Nancy
  • .net获取当前url各种属性(文件名、参数、域名 等)的方法
  • [100天算法】-每个元音包含偶数次的最长子字符串(day 53)
  • [Android]一个简单使用Handler做Timer的例子