当前位置:首页 > 科技  > 软件

useCallback 使用的四个阶段,你都知道吗?

来源: 责编: 时间:2024-01-18 09:40:32 320观看
导读非 React 使用者估计看了都要摇头啊。一个破回调函数的运用,居然能折腾出来这么多事。一大堆文章都在探讨如何使用它更合理。事实上确实如此,在 React 独特的单向数据流刷新机制下,对于 useCallback 认知的逐渐深入实际

DdF28资讯网——每日最新资讯28at.com

非 React 使用者估计看了都要摇头啊。一个破回调函数的运用,居然能折腾出来这么多事。一大堆文章都在探讨如何使用它更合理。事实上确实如此,在 React 独特的单向数据流刷新机制下,对于 useCallback 认知的逐渐深入实际上也代表着对 React 本身这个机制的理解更进一步,因此在你彻底消化 React 刷新机制之前,这个过程中的每一个知识点可能都有巨大的探讨空间DdF28资讯网——每日最新资讯28at.com

前几天我的一位学生跟我探讨了一种 useCallback 的用法,他的想法是:当我们在封装开源工具库时,对自定义 hook 中暴露出来的钩子函数使用 useCallback 缓存。因为我们并不确定使用者是否需要一个引用稳定的钩子函数,他们有可能是需要的,因此用 useCallback 来包一层是有意义的。但是他并不确定这样的做法是否合适,是否具备较大的正向收益。DdF28资讯网——每日最新资讯28at.com

那么我就借着这个案例,来跟大家探讨一下,我们在 React 进阶的过程中,使用 useCallback 的四个阶段。DdF28资讯网——每日最新资讯28at.com

阶段一:敬畏

这个时候你还是一个初学者,对 React 的理解还不够深刻不够全面,但是常常看到文章,或者听别人说 useCallback 跟性能优化有关,可对于你而言,你并不是非常清楚它跟性能优化的具体关系在哪里,想知道,但不知道或者不够确定,因此对这个 hook api 有一种敬畏之心,各个论坛里对于 useCallback 的介绍很多很嘲杂,但你不敢随便用。DdF28资讯网——每日最新资讯28at.com

因此你很想去看看别人的代码里,useCallback 是怎么用的,是在什么场景下使用的,但是想要看到别人的代码也并不容易,因此你可能会在这个阶段徘徊。DdF28资讯网——每日最新资讯28at.com

阶段二:懂了

随着学习的深入,你逐渐开始深入理解了 React 的单向数据流机制,也对 React 的使用更加熟练,知道 React 经常会存在许多 re-render,你终于搞懂了 useCallback 的使用场景,它结合 React.memo 能够缓存组件,避免组件的冗余 re-render。DdF28资讯网——每日最新资讯28at.com

于是你在项目中大量的使用了他们,就像当初 PureComponent 一样,你恨不得每个函数都用 useCallback 套一层,以确保自己的项目能最大限度减少 re-render,从而达到一个极致的性能体验。DdF28资讯网——每日最新资讯28at.com

function App() {  ...  const clickHankler = useCallback(() => {    ...  }, [count])  const onOpen = useCallback(() => {    ...  }, [])    ...}

但是不管你用还不用,是大量使用还是大量不使用,从页面的运行结果中,都看不出来你这样写带来了什么实质的提升,甚至你有可能在依赖项的使用上感到难受,因为闭包的影响导致实际运行结果跟你预想的有出入。但是你能明确感受到 re-render 次数减少了。因此这个阶段你非常坚信自己达到了性能优化的目的。DdF28资讯网——每日最新资讯28at.com

直到一次偶然的面试中,你被面试官一个问题问得哑口无言:只用 useCallback 能达到减少 re-render 的次数吗?为什么?DdF28资讯网——每日最新资讯28at.com

阶段三:精通DdF28资讯网——每日最新资讯28at.com

听了我的直播分享,彻底搞懂了 React 的底层 DIFF 机制,你发现原来在 React 底层机制的逻辑下,我们大量的缓存工作其实是没有必要的。React.memo 也有不小的使用成本,有的时候他的损耗不一定比 re-render 更低,于是你懂得了如何在项目中合理的使用 useCallback + React.memo,一通优化下来,项目里的 useCallback 都被删得差不多了,只在关键位置剩下几个。DdF28资讯网——每日最新资讯28at.com

优化的结果很理想,re-render 的情况不仅没有变多,项目还减负了,性能又得到了提升,你很开心很有成就感。心想我终于又有了成长,再次遇到上次那个面试官,我必定能吊打他。DdF28资讯网——每日最新资讯28at.com

阶段四:贯通DdF28资讯网——每日最新资讯28at.com

你终于明白了 useCallback 只是一个非常普通的记忆函数。在 React hooks 特定的机制下记忆函数本身就被大量运用。React 的许多 hook 都有类似的记忆能力,useCallback 只是最普通的那一个,另外的 hook 都在记忆能力的基础之上又添加了一些别的语义。DdF28资讯网——每日最新资讯28at.com

useStateuseEffectuseLayoutEffectuseCallbackuseMemouseRefuseReduceruseSyncExternalStore...

这个阶段你不再特殊看待他,在你的知识结构里面你也不再特意的把他跟性能优化挂上勾,而是把他标记为一个记忆函数,他能够保持一个函数的引用,当你在 React 这个不稳定的上下文环境中过,需要一个稳定的引用时,你才会使用 useCallback。DdF28资讯网——每日最新资讯28at.com

因此,当你在封装一个开源工具库时,你想到了你会对外抛出一个钩子函数,但是你并不确定使用者会如何使用这个钩子函数,使用者有可能会把他传递给子组件,此时如果钩子函数引用不稳,那么就有可能导致子组件 re-render。DdF28资讯网——每日最新资讯28at.com

例如在我们前面学习自定义 hook 的文章中,我们封装了一个 hook useFetch,代码如下:DdF28资讯网——每日最新资讯28at.com

import { useState, useRef, useLayoutEffect } from 'react'type API<T, P> = (param?: P) => Promise<T>export default function useFetch<T, P>(api: API<T, P>) {  const param = useRef<P>()  const [list, setList] = useState<T>()  const [error, setError] = useState('')  const [loading, setLoading] = useState(true)  function getList() {    api(param.current).then(res => {      setList(res)      setLoading(false)      setError('')    }).catch(err => {      setLoading(false)      setError(err)    })  }  useLayoutEffect(() => {    loading && getList()  }, [loading])  return {     param,     setParam: (p: P) => param.current = p,    list,     error,     loading,     setLoading   }}

我们可以看到代码里,在这个自定义 hook 中,返回了两个钩子函数 setLoading setParam。DdF28资讯网——每日最新资讯28at.com

为了验证他们的引用是否稳定,我们在使用 useFectch 的组件中使用如下代码来验证函数的引用是否发生了变化。DdF28资讯网——每日最新资讯28at.com

useEffect(() => {  console.log('setLoading')}, [setLoading])

验证结果非常神奇,setLoading 的引用居然非常的稳定。但对于此时的你来说,这并没有什么值得奇怪的地方。因为他是直接从 useState 中获取出来的。useState 本身就具备记忆能力,因此对于 setLoading 来说,我们不再需要想任何办法来让他的引用来保持稳定。DdF28资讯网——每日最新资讯28at.com

setParam 跟预期一样,一点也不稳定,每次状态变化,他的引用都会发生变化。因为在定义它的时候,每次都是新生成的函数给他赋值。DdF28资讯网——每日最新资讯28at.com

return {     param, +    setParam: (p: P) => param.current = p,    list,     error,     loading,     setLoading   }

此时到了 useCallback 大展身手的时候了,我们使用 useCallback 包一层。DdF28资讯网——每日最新资讯28at.com

return {     param, -    setParam: (p: P) => param.current = p,+    setParam: useCallback((p: P) => param.current = p, []),    list,     error,     loading,     setLoading   }

再次验证,发现引用果然变稳定了。nice。DdF28资讯网——每日最新资讯28at.com

但是你害怕这样做有什么你没想到的点,因为 useCallback 太善变了,所以你就跑来跟我沟通,想确定一下这样子做到底能不能带来很大的正向收益。DdF28资讯网——每日最新资讯28at.com

万万没想到,我一开口就说:没必要。DdF28资讯网——每日最新资讯28at.com

我引导你去看一下引用稳定的 setLoading 是如何使用的,你就去翻了一下代码,结果一看,坏事了,setLoading 因为传了一个参数,导致在使用的时候又套了一层函数。DdF28资讯网——每日最新资讯28at.com

代码如下。此时 onClick 接收到的还是一个引用不稳定的匿名函数... setLoading 的引用白考虑了。DdF28资讯网——每日最新资讯28at.com

<Button  className={s.button}  onClick={() => setLoading(true)}>

然后你又看了一眼 setParam 的使用,还是这么个情况。DdF28资讯网——每日最新资讯28at.com

<input  className={s.input}  placeholder="请输入您要搜索的内容"  onChange={(e) => setParam(e.target.value)}/>

最后一想,发现好像 useCallback 又做了无用功。DdF28资讯网——每日最新资讯28at.com

至此,你彻底悟了。DdF28资讯网——每日最新资讯28at.com

就说总有一种不确定感,原来少考虑了一步。当自定义 hook 传出来的 函数在执行时需要传入参数时,就不得不在这个函数外面包一层匿名函数,再传递给子组件使用,如果它不需要参数,useCallback 才会发挥它的效果。DdF28资讯网——每日最新资讯28at.com

function useRouter() {  const { dispatch } = useContext(RouterStateContext);  const navigate = useCallback((url) => {    dispatch({ type: 'navigate', url });  }, [dispatch]);  const goBack = useCallback(() => {    dispatch({ type: 'back' });  }, [dispatch]);  return {    navigate,    goBack,  };}
const {goBack} = useRouter()... <Child onBack={goBack}  />

当真是真是步步惊心啊。DdF28资讯网——每日最新资讯28at.com

你终于悟到了要结合实际使用的场景去考虑使用 useCallback 的准确时机,自此,融汇贯通成就达成。DdF28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-64102-0.htmluseCallback 使用的四个阶段,你都知道吗?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 掌握Python八种绘图类型带你深入时间序列数据分析

下一篇: 2024年不容错过的十个开发框架

标签:
  • 热门焦点
  • 石头自清洁扫拖机器人G10S评测:多年黑科技集大成之作 懒人终极福音

    科技圈经常能看到一个词叫“缝合怪”,用来形容那些把好多功能或者外观结合在一起的产品,通常这样的词是贬义词,但如果真的是产品缝合的好、缝合的实用的话,那它就成了中性词,今
  • 7月安卓手机性能榜:红魔8S Pro再夺榜首

    7月份的手机市场风平浪静,除了红魔和努比亚带来了两款搭载骁龙8Gen2领先版处理器的新机之外,别的也想不到有什么新品了,这也正常,通常6月7月都是手机厂商修整的时间,进入8月份之
  • K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • Java NIO内存映射文件:提高文件读写效率的优秀实践!

    Java的NIO库提供了内存映射文件的支持,它可以将文件映射到内存中,从而可以更快地读取和写入文件数据。本文将对Java内存映射文件进行详细的介绍和演示。内存映射文件概述内存
  • 破圈是B站头上的紧箍咒

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之每年的暑期档都少不了瞄准追剧女孩们的古偶剧集,2021年有优酷的《山河令》,2022年有爱奇艺的《苍兰诀》,今年却轮到小破站抓住了追
  • 腾讯VS网易,最卷游戏暑期档,谁能笑到最后?

    作者:无锈钵来源:财经无忌7月16日晚,上海1862时尚艺术中心。伴随着幻象的精准命中,硕大的荧幕之上,比分被定格在了14:12,被寄予厚望的EDG战队以绝对的优势战胜了BLG战队,拿下了总决
  • 花7万退货退款无门:谁在纵容淘宝珠宝商家造假?

    来源:极点商业作者:杨铭在淘宝购买珠宝玉石后,因为保证金不够赔付,店铺关闭,退货退款难、维权无门的比比皆是。&ldquo;提供相关产品鉴定证书,支持全国复检,可以30天无理由退换货。&
  • 苹果公司要求三星和LG Display生产「无边框」OLED iPhone显示屏

    据 The Elec 报道,苹果已要求其供应商为未来的 iPhone 型号开发「无边框」OLED 显示面板。苹果显然已要求三星和 LG Display 开发新的 OLED 显示面
  • 苹果140W USB-C充电器:采用氮化镓技术

    据10 月 30 日 9to5 Mac 消息报道,当苹果推出新的 MacBook Pro 2021 时,该公司还推出了新的 140W USB-C 充电器,附赠在 MacBook Pro 16 英寸机型的盒子里,也支
Top