React——useCallback

一、定义:

useCallback是一个允许你在多次渲染中缓存函数的 React Hook。它返回一个记忆化的回调函数,只有在依赖项改变时才会更新。这有助于避免在每次渲染时都创建新的函数实例,特别是在将回调函数传递给子组件时。

二、形式:

useCallback(function,dependencies)

参数:

function

1、定义:想要缓存的函数

2、特点:

  • 可以接受任何参数并返回任何值
  • React只会把这个函数返回给你,而不是直接调用!!(由你自己决定何时调用)
  • 进行下一次渲染时,dependencies没有变化,则funtion返回相同的函数;若有变化,React将新传入的函数缓存以便后续使用

dependencies

1、定义:有关是否更新function的所有响应值的一个列表

2、特点:

  • 响应式值包括 props、state,和所有在你组件内部直接声明的变量和函数。
  • 依赖列表必须具有确切数量的项,并且必须像 [dep1, dep2, dep3] 这样编写
  • React 使用 Object.is 比较每一个依赖和它的之前的值。

返回值:

在初次渲染时,useCallback 返回你已经传入的 function 函数

在之后的渲染中, 如果依赖没有改变,useCallback 返回上一次渲染中缓存的 function 函数;否则返回这一次渲染传入的 function。

三、注意点

1、useCallback 是一个 Hook,所以应该在 组件的顶层 或自定义 Hook 中调用(不应在循环或者条件语句中调用它)

原因:这样做是为了确保 Hook 的调用顺序在每次渲染中是一致的。React 使用这个顺序来跟踪每个 Hook 的状态和效果。

应对措施:如果你需要在循环或者条件语句中调用它,正确的办法应该是新建一个组件,并将state移入其中

2、useCallback 是一个有用的性能优化工具,但在某些情况下(尤其是在开发和特定的生产场景中),其缓存可能会被丢弃

四、用法

1、跳过组件的重新渲染

背景:默认情况下,当一个组件重新渲染时, React 将递归渲染它的所有子组件,有时会导致重新渲染的很慢

初始解决:

将子组件包裹在memo中,例:

import { memo } from 'react';
const ShippingForm = memo(function ShippingForm({ onSubmit }) {// ...
});

好处:如果prop与上一次渲染时相同,这个子组件将跳过重新渲染

不足:在 JavaScript 中,function () {} 或者 () => {} 总是会生成不同的函数,所以因某个变量更改导致组件重新渲染时,那么作为prop传入子组件的函数每次都不一样时,这也导致了memo对性能的优化永远不会生效

优化解决:

思路:将作为prop传入的子函数传递给useCallback,这样可以确保它在多次重新渲染之间是相同的函数(除非是依赖项发生变化它才会变化),也就能让memo知道这时该prop与上次没有变化,子组件也就不会重新渲染。

注:useCallback只应用作性能优化,除非出于某种特定原因,否则不必将一个函数包裹在 useCallback 中

useCallback应用场景:

  • 将其作为 props 传递给包装在 [memo] 中的组件。如果 props 未更改,则希望跳过重新渲染。缓存允许组件仅在依赖项更改时重新渲染。
  • 传递的函数可能作为某些 Hook 的依赖。比如,另一个包裹在 useCallback 中的函数依赖于它,或者依赖于 useEffect 中的函数。

注意:useCallback不会阻止创建函数,你总是在创建一个函数(这很好!),但是如果没有任何东西改变,React会忽略它并返回缓存的函数

2、从记忆回调中更新state

背景:想实现在useCallback回调中基于之前的state来更新state

例:

做法一:指定依赖项

function TodoList() {const [todos, setTodos] = useState([]);const handleAddTodo = useCallback((text) => {const newTodo = { id: nextId++, text };setTodos([...todos, newTodo]);}, [todos]);

做法二:使用updater function

function TodoList() {const [todos, setTodos] = useState([]);const handleAddTodo = useCallback((text) => {const newTodo = { id: nextId++, text };setTodos(todos => [...todos, newTodo]);}, []); // ✅ 不需要 todos 依赖项

在 React 中,updater function 是一种用于更新组件状态的函数,它允许你基于当前状态计算下一个状态,而不需要将当前状态作为依赖项传useCallback。

好处:

  • 可以帮助减少依赖项的数量,从而避免不必要的重新渲染。
  • 可以确保在状态更新时始终使用最新的状态值。尤其在处理异步操作时,直接引用 状态值 可能会导致不一致的结果。

3、防止频繁触发Effect

背景:想要在Effect内部调用函数

例:聊天室的案例

function ChatRoom({ roomId }) {const [message, setMessage] = useState('');function createOptions() {return {serverUrl: 'https://localhost:1234',roomId: roomId};}
useEffect(() => {const options = createOptions();const connection = createConnection(options);connection.connect();return () => connection.disconnect();}, [createOptions]); // 🔴 问题:这个依赖在每一次渲染中都会发生改变//....

声明依赖的必要性:在 useEffect 中,如果你依赖的变量(例如 createOptions)没有被声明为依赖,React 将无法检测到这些变量的变化。这可能导致副作用使用过时的数据。

声明依赖存在潜在问题:在聊天室的例子中,这意味着每当 createOptions 变化时,useEffect 可能会重新连接到聊天室。假设连接的逻辑在 useEffect 中,这样就会造成一个循环:每次连接后,如果 createOptions 发生变化,就会重新连接,导致不断地连接和断开。

解决方法:在 Effect 中将要调用的函数包裹在 useCallback 中:

 const createOptions = useCallback(() => {return {serverUrl: 'https://localhost:1234',roomId: roomId};}, [roomId]); // ✅ 仅当 roomId 更改时更改
这将确保如果 roomId 相同,createOptions 在多次渲染中会是同一个函数

优化解决方法:消除对函数依赖项的需求,将函数(例createOptions)移入 Effect 内部

function ChatRoom({ roomId }) {const [message, setMessage] = useState('');useEffect(() => {function createOptions() { // ✅ 无需使用回调或函数依赖!return {serverUrl: 'https://localhost:1234',roomId: roomId};}const options = createOptions();const connection = createConnection(options);connection.connect();return () => connection.disconnect();}, [roomId]); // ✅ 仅当 roomId 更改时更改

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/60105.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu常见命令

关于export LD_LIBRARY_PATHcmake默认地址CMakelists.txt知识扩充/home:挂载新磁盘到 /home 子目录 关于export LD_LIBRARY_PATH 程序运行时默认的依赖库的位置包括lib, /usr/lib ,/usr/local/lib 通过命令export LD_LIBRARY_PATHdesired_path:$LD_LIBRARY_PATH追加…

时间类的实现

在现实生活中,我们常常需要计算某一天的前/后xx天是哪一天,算起来十分麻烦,为此我们不妨写一个程序,来减少我们的思考时间。 1.基本实现过程 为了实现时间类,我们需要将代码写在3个文件中,以增强可读性&a…

php交友源码交友系统源码相亲交友系统源码php社交系统php婚恋源码php社区交友源码vue 仿交友社交语聊技术栈

关于PHP交友、相亲、婚恋、社区交友系统的源码以及Vue仿交友社交语聊技术栈,以下是一些详细信息和建议: 一、PHP交友系统源码 系统架构设计 前端展示层:负责向用户提供直观友好的界面,包括注册登录页面、个人资料页面、匹配页面、…

Java小技艺

使用bat文件启动jar包 平时在工作中运行jar包一般是导出后命令行窗口运行 jar -jar xxx.jar 这个其实是很不方便的。可以在win上编写bat脚本去运行jar包的。 1 编写bat脚本 start jre8/bin/javaw -jar xxxx.jar2 将jre和待执行的jar包存放到一个目录下(和bat文件在同一目录…

蓝桥杯第22场小白入门赛2~5题

这场比赛开打第二题就理解错意思了,还以为只能用3个消除和5个消除其中一种呢,结果就是死活a不过去,第三题根本读不懂题意,这蓝桥杯的题面我只能说出的是一言难尽啊。。第四题写出来一点但是后来知道是错了,不会正解&am…

‘视’不可挡:OAK相机助力无人机智控飞行!

南京邮电大学通达学院的刘同学用我们的oak-d-lite实现精确打击无人机的避障和目标识别定位功能,取得了比赛冠军。我们盼望着更多的朋友们能够加入到我们OAK的队伍中来,参与到各式各样的比赛中去。我们相信,有了我们相机的助力,大家…

最小生成树——Kruskal、Prim算法

图的存储: 高阶数据结构——图 文章目录 目录 文章目录 一、kruskal算法 二、Prim算法 前言 连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树 就不在连通;反之&#xf…

集群聊天服务器(9)一对一聊天功能

目录 一对一聊天离线消息服务器异常处理 一对一聊天 先新添一个消息码 在业务层增加该业务 没有绑定事件处理器的话消息会派发不出去 聊天其实是服务器做一个中转 现在同时登录两个账号 收到了聊天信息 再回复一下 离线消息 声明中提供接口和方法 张三对离线的李…

华为再掀技术革新!超薄膜天线设计路由器首发!

随着Wi-Fi技术的不断进步,新一代的Wi-Fi 7路由器凭借其高速率、低延迟、更稳定的性能受到了广泛关注。它能够更好地满足现代家庭对网络性能的高要求,带来更加流畅、高效的网络体验。9月24日,华为在其秋季全场景新品发布会上推出了全新Wi-Fi 7…

【阅读记录-章节2】Build a Large Language Model (From Scratch)

目录 2.Working with text data2.1 Understanding word embeddings2.2 Tokenizing text通过一个简单的实验来理解文本的词元化概念关键概念 2.3 Converting tokens into token IDs实现分词器类(Tokenizer Class)应用分词器测试文本的编码与解码通过分词器…

SDF,一个从1978年运行至今的公共Unix Shell

关于SDF 最近发现了一个很古老的公共Unix Shell服务器,这个项目从1978年运行至今,如果对操作系统,对Unix感兴趣,可以进去玩一玩体验一下 SDF Public Access UNIX System - Free Shell Account and Shell Access 注册方式 我一…

关于Qt C++中connect的几种写法

目录 1. 传统的槽函数写法 2. 使用函数指针的connect写法(5.0) 3. Lambda表达式作为槽函数(C11) 4.使用QOverload选择重载信号的写法 这connect函数就像是编程世界里的“茴”字,千变万化,各有千秋。咱们…

反向代理模块

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求,然后将请求转发给内部网络上的服务器,将从服务器上得到的结果返回给客户端,此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说,反向代理就相当于…

用jquery做一个websocket客户端

先看效果图&#xff1a; 功能很简单&#xff0c;就是作为客户端连接websocket&#xff0c;并实现接受和发送消息。具体代码如下&#xff1a; <!DOCTYPE html> <html lang"zh-cn"> <head><meta charset"UTF-8"><meta name"…

抽象java入门1.5.3.2——类的进阶(中)

前期回顾&#xff1a;抽象java入门1.5.3.1——类的进阶https://blog.csdn.net/c_yanxin_ru/article/details/140858898?spm1001.2014.3001.5501 总结&#xff1a; 在代码溯源中&#xff0c;我发现了一个奇怪的东西&#xff0c;就是OUT不是类中类&#xff08;不是常规类的写法…

蓝桥杯每日真题 - 第17天

题目&#xff1a;&#xff08;最大数字&#xff09; 题目描述&#xff08;13届 C&C B组D题&#xff09; 题目分析&#xff1a; 操作规则&#xff1a; 1号操作&#xff1a;将数字加1&#xff08;如果该数字为9&#xff0c;变为0&#xff09;。 2号操作&#xff1a;将数字…

Ease Monitor 会把基础层,中间件层的监控数据和服务的监控数据打通,从总体的视角提供监控分析

1. 产品定位 Ease Monitor 有如下的产品定位&#xff1a; 关注于整体应用的SLA。 主要从为用户服务的 API 来监控整个系统。 关联指标聚合。 把有关联的系统及其指示聚合展示。主要是三层系统数据&#xff1a;基础层、平台中间件层和应用层。 快速故障定位。 对于现有的系统…

3D Gaussian Splatting 代码层理解之Part2

现在让我们来谈谈高斯分布。我们已经在Part1介绍了如何根据相机的位置获取 3D 点并将其转换为 2D。在本文中,我们将继续处理高斯泼溅的高斯部分,这里用到的是代码库 GitHub 中part2。 我们在这里要做的一个小改动是,我们将使用透视投影,它利用与上一篇文章中所示的内参矩阵…

一道算法期末应用题及解答

1&#xff0e;印刷电路板布线区划分成为n m 个方格&#xff0c;确定连接方格a 到方格b 的最短布线方案。 在布线时&#xff0c;只能沿直线或者直角布线&#xff0c;为避免交叉&#xff0c;已经布线的方格做了封锁标记&#xff0c;其他线路不允许穿过被封锁的方格&#xff0c;某…