React Hook优化props传入func造成的重复渲染

前言

  • 在React框架下, 如果不做任何优化, 父组件渲染会触发子组件的重新render.
  • 但由于协调算法的优化, 触发render不一定触发真实dom的更新.
  • 但如果出现大量子组件重复render, 那么我们依旧可以考虑优化子组件渲染过程, 以获得可观的性能提升.

待优化示例

链接地址: https://codesandbox.io/s/hook-children-component-preformance1-es6bu

在这里插入图片描述

  • 这是一个由多个小方格组成的棋盘, 点击小方格变为绿色, 再次点击变为灰色.
  • 每次点击, 理论上只有当前点击的小方格需要更新, 但实际上, 其他的小方格也会重新render.
  • 其他小方格重新render的原因是, 我们为了方便状态管理, 进行了状态提升, 将所有小方格的状态提升到了共有的父组件, 导致某些传入小方格的props的引用值发生了改变.
  • 引用发生改变但引用的实际值可能并没有发生改变, 所以我们可以针对这一部分引用进行优化以达到优化组件重复渲染的目的.

解决思路

memo优化
const Block = memo((props) => {
  const { handleBlockClick } = props
  // 此处省略组件逻辑
  return <div></div>;
});
  • 使用memo包装Block组件, 但由于外部传入的handleBlockClick函数每次会重新生成, 所以Block组件依旧会重新render.
使用useCallback
const handleBlockClick = useCallback((blockIndex) => {
  const newState = set(
    state,
    [blockIndex, "checked"],
    !state[blockIndex].checked
  );
  setState(newState);
}, [])
  • 我们可以尝试使用useCallback处理handleBlockClick, handleBlockClick函数不再发生更新, 所以也不会再触发其他Block组件的重新渲染.
  • 但由于内部所依赖的闭包变量state也不再发生更新, 会导致每一次点击都是在初始化的state上进行更改, 所以点击多次后界面只会保留最后点击的绿色方块.


    在这里插入图片描述
使用useRef
  • 我们可以尝试使用useRef生成一个固定引用以挂载handleBlockClick函数, 这样handleBlockClick可以正常更新, 且useRef生成的引用并不会发生变更.
const handleBlockClick = (blockIndex) => {
  const newState = set(
    state,
    [blockIndex, "checked"],
    !state[blockIndex].checked
  );
  setState(newState);
}

// 这里请注意, 不能直接使用useRef(handleClick) 替代下面两行,
// 因为useRef中的参数是初始值, 并不会在每次执行useRef时更新ref.current.
const newRef = useRef()
newRef.current = handleBlockClick

// Block组件传入newRef
<Block
  onClick={newRef}
/>
去掉current
  • 使用useRef解决了Block组件重新渲染的问题, 并且也解决了useCallback造成的state无法更新的问题.
  • 但Block组件内部需要以onClick.current()的形式调用外部传入的函数. 所以这里我们可以实现一个闭包函数以实现Block组件可以以onClick()的方式直接调用传入的函数, 去掉current后缀:
const newRefTarget = (...arr) => {
  newRef.current.apply(null, arr)
}
  • 然后使用useCallback处理闭包函数以确保newRefTarget不会重新生成.
const newRefTarget = useCallback(() => {
  newRef.current.apply(null, arguments)
}, [])

<Block
  onClick={newRefTarget}
/>
抽离逻辑为新的hooks
  • 最后, 我们可以对之前优化handleBlockClick的逻辑进行抽离, 组装为一个新的hooks, 方便其他传入子组件的function复用:

const useFunction = (callback) => {
  const newRef = useRef()
  newRef.current = callback

  return useCallback((...arr) => {
    newRef.current.apply(null, arr)
  }, [])
}

// Board组件中则使用useFunction生成的绑定索引的handleBlockClick
const handleBlockClick = useFunction((blockIndex) => {
  const newState = set(
    state,
    [blockIndex, "checked"],
    !state[blockIndex].checked
  );
  setState(newState);
})

<Block
  onClick={handleBlockClick}
/>
优化后示例

链接: https://codesandbox.io/s/hook-children-component-preformance2-w0pj5

小结
  • 使用memo使Block组件具备浅比较后优化渲染的特性
  • 使用useRef创建固定索引挂载handleBlockClick函数
  • 使用闭包函数优化useRef产生的current后缀问题
  • 使用useCallback优化闭包函数使其保持不变
  • 抽离优化逻辑为新的useFunction Hook
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • React Hooks Hook是React v16.8的新特性,可以用函数的形式代替原来的继承类的形式,可以在不...
    左冬的博客阅读 763评论 0 1
  • 在React v16.8新增了Hook,它提供了在函数组件中访问状态和React生命周期等能力,这些函数可以在程序...
    小小小小小粽子阅读 477评论 0 0
  • 什么是 Hooks 以往,React 组件都是通过 class 的形式来编写,只有无状态组件才可以用函数来编写。H...
    Actoress阅读 1,086评论 0 0
  • 平常在完成业务功能开发后,不知道你是否还会精益求精,做一些性能优化方面的工作呢?React框架中有一些性能优化相关...
    SCQ000阅读 1,348评论 0 52
  • > 本文重点:介绍React重构的起因和目的,理解Fiber tree单向链表结构中各属性含义,梳理调度过程和核心...
    intopiece_槟阅读 1,171评论 0 0