位置: IT常识 - 正文

React避免组件重复渲染(react避免子组件更新)

编辑:rootadmin
##前言 在 React 开发中如果不去管组件的重复渲染问题,项目稍微复杂一点性能将不堪入目,下面将介绍项目中最常见的例子及解决方案(仅 hooks 组件)。 ##预先了解所用知识 React.memo React.useCallback React.useMemo useMemoizedFn(利用 ... 前言

推荐整理分享React避免组件重复渲染(react避免子组件更新),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:react 组件销毁,react 组件销毁,react 组件销毁,react组件必不可少的函数,React避免组件重渲染,react组件重新加载,React避免组件重渲染,react避免子组件更新,内容如对您有帮助,希望把文章链接给更多的朋友!

在 React 开发中如果不去管组件的重复渲染问题,项目稍微复杂一点性能将不堪入目,下面将介绍项目中最常见的例子及解决方案(仅 hooks 组件)。

预先了解所用知识React.memoReact.useCallbackReact.useMemouseMemoizedFn(利用 ref + useMemo 配合单例模式实现比 useCallback 更加好用的 hook,下面会提,强烈推荐)

没错,只需使用上面4点即可解决大多数组件重复渲染问题,而如何合理去使用呢?

例1: 父组件状态更新导致子组件无效渲染const Parent = () => { const [count, setCount] = useState(0); const [son1Count, setSon1Count] = useState(0); const [son2Count, setSon2Count] = useState(0); return ( <div> {console.log("Parent render")} <button onClick={() => setCount(v => v + 1)}>Parent + 1</button> <button onClick={() => setSon1Count(v => v + 1)}>Son1 + 1</button> <button onClick={() => setSon2Count(v => v + 1)}>Son2 + 1</button> <h3>Parent: {count}</h3> <Son1 son1Count={son1Count} /> <Son2 son2Count={son2Count} /> </div> );};const Son1 = (props) => { return ( <div> {console.log("Son1 render")} Son1: {props.son1Count} </div> );};const Son2 = (props) => { return ( <div> {console.log("Son2 render")} Son2: {props.son2Count} </div> );};

可以看出,无论改变哪个值,所有组件都重新渲染了,理想情况是改变 count 值实际并不需要 Son1 和 Son2 组件重新渲染,改变 son1Count 也不需要 Son2 重新渲染,简而言之就是子组件所依赖的值没发生变化就不需要重新渲染,上面情况只需将子组件用 React.memo 包裹即可:

const Son1 = React.memo((props) => { return ( <div> {console.log("Son1 render")} Son1: {props.son1Count} </div> );});const Son2 = React.memo((props) => { return ( <div> {console.log("Son2 render")} Son2: {props.son2Count} </div> );});React避免组件重复渲染(react避免子组件更新)

可以看到轻松包裹一下就已经达到理想效果,React.memo 本质就是一个高阶组件(HOC),通过浅比较(比较栈内存中的值) props、state 和 render 的内容来判断是否需要重新渲染组件。而有时候子组件不得不依赖父组件的值,但这个依赖的值发生改变又不需要重新渲染组件怎么办,接着看下面例2。

例2: 自定义子组件是否重新渲染const Parent = () => { const [random, setRandom] = useState(Math.random()); const [nowTime, setNowTime] = useState(new Date().toLocaleString()); const timerRef = useRef(); useEffect(() => { timerRef.current = setInterval(() => { setRandom(Math.random()); }, 1000); return () => { clearInterval(timerRef.current); }; }); return ( <div> {console.log("Parent render")} <h3>random: {random}</h3> <button onClick={() => setNowTime(new Date().toLocaleString())}> 点击更新子组件 </button> <Son random={random} nowTime={nowTime} /> </div> );};const Son = React.memo( (props) => { return ( <div> {console.log("Son render")} <p> {props.nowTime}父组件 random 值为: {props.random} </p> </div> ); });

上面例子中,子组件依赖父组件 random 和 nowTime,而子组件理想是在点击按钮 nowTime 发生改变后 才重新渲染组件显示 random 的值,不需要上面那样实时渲染,此时只需要在 React.memo 第二个参数自定义渲染规则即可,React.memo 第二个参数为可选参数:

function memo<P extends object>( Component: FunctionComponent<P>, propsAreEqual?: (prevProps: Readonly<PropsWithChildren<P>>, nextProps: Readonly<PropsWithChildren<P>>) => boolean): NamedExoticComponent<P>;

看函数签名可知,第二个参数可传一个回调函数,回调函数会有两个形参,props 状态改变前的值和改变后的值,返回值为一个布尔值,true 禁止重新渲染,false 将重新渲染,和以往 class 组件中的 shouldComponentUpdate 生命周期钩子用法很像,下面加入自定义渲染判断条件,即只在 props.nowTime 发生改变时进行重新渲染:

const Son = React.memo( (props) => { return ( <div> {console.log("Son render")} <p> {props.nowTime}父组件 random 值为: {props.random} </p> </div> ); }, (prevProps, nextProps) => prevProps.nowTime === nextProps.nowTime);

从上图可见只有 props.nowTime 发生改变才会重新渲染。第二个参数的判断规则在实际开发中还有很多妙用,比如一些情况可以替代 useCallback,后面会提一下。

例3: 向子组件传递方法(状态提升)导致子组件无效渲染const Parent = () => { const [count, setCount] = useState(0); const [sonCount, setSonCount] = useState(0); const allPlus = () => { setCount(v => v + 1); setSonCount(v => v + 1); }; return ( <div> {console.log("Parent render")} <button onClick={() => setCount(v => v + 1)}>Parent + 1</button> <h3>Parent: {count}</h3> <Son allPlus={allPlus} sonCount={sonCount} /> </div> );};const Son = React.memo((props) => { return ( <div> {console.log("Son render")} <p>Son: {props.sonCount}</p> <button onClick={props.allPlus}>All + 1</button> </div> );});

从控制台输出可以看见,当传递一个方法给子组件时,就算使用 React.memo 进行包裹也不济于事,导致该问题出现的根本原因是当 count 发生改变导致父组件发生重新渲染,Parent 组件本质也是一个函数,所以 const allPlus = () => { setCount(count + 1); setSonCount(sonCount + 1); }; 也会重新执行一次,这就意味着 allPlus 已经重新赋值,此时虽然看起来没什么变化,实际上 allPlus 在栈内存中的地址已经改变,而 React.memo 默认正是通过浅比较决定是否重新渲染,也就导致只要父组件发生重绘,子组件一定会跟着无效重绘。使用 React.useCallback 缓存函数解决子组件无效渲染,只需将上面 allPlus 方法使用 useCallback 包裹:

const allPlus = useCallback(() => { setCount(v => v + 1); setSonCount(v => v + 1); }, []);

function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;

从 useCallback 函数签名看到,需要传递两个参数,第一个传需要缓存的函数,第二个是依赖值,其本质就是返回的是一个 memoized(缓存)函数,在依赖不变的情况下,多次定义的时候,返回的值是相同的,他的实现原理是当使用一组参数初次调用函数时,会缓存参数和计算结果,当再次使用相同的参数调用该函数时,会直接返回相应的缓存结果,上面例子依赖值为空数组,所以无论怎样 allPlus 栈内存值都不会变,也就不会触发子组件无效重绘。在向子组件传递方法时在项目中很多时候会懒得专门

本文链接地址:https://www.jiuchutong.com/zhishi/304635.html 转载请保留说明!

上一篇:织梦DEDECMS栏目后台设置隐藏,前台栏目如何显示(织梦如何给栏目增加缩略图)

下一篇:python中datetime和字符串之间如何转换(python中datetime用法)

  • 电脑上字体怎么调节大小(电脑上字体怎么换字体)

    电脑上字体怎么调节大小(电脑上字体怎么换字体)

  • 苹果11手机悬浮球在哪里设置关闭(苹果11手机悬浮球怎么设置)

    苹果11手机悬浮球在哪里设置关闭(苹果11手机悬浮球怎么设置)

  • 苹果12什么时候发售(苹果12什么时候出来)

    苹果12什么时候发售(苹果12什么时候出来)

  • word字体20磅怎么设置(word文字设置为20磅怎么设置)

    word字体20磅怎么设置(word文字设置为20磅怎么设置)

  • 辅助功能快捷键干嘛的(苹果手机如何关闭辅助功能快捷键)

    辅助功能快捷键干嘛的(苹果手机如何关闭辅助功能快捷键)

  • 笔记本重装系统会变盗版吗(笔记本重装系统需要多久)

    笔记本重装系统会变盗版吗(笔记本重装系统需要多久)

  • 苹果无线耳机怎么暂停播放音乐(苹果无线耳机怎么设置)

    苹果无线耳机怎么暂停播放音乐(苹果无线耳机怎么设置)

  • 20g流量能看多久视频(20g流量能看多久快手)

    20g流量能看多久视频(20g流量能看多久快手)

  • 开启永恒模式什么意思(开启永恒模式性能会增强吗)

    开启永恒模式什么意思(开启永恒模式性能会增强吗)

  • 经常开热点对手机损吗(经常开热点对手机电池的影响)

    经常开热点对手机损吗(经常开热点对手机电池的影响)

  • 直播可以看到观众脸吗(直播可以看到观看时长吗)

    直播可以看到观众脸吗(直播可以看到观看时长吗)

  • 网络mac是什么意思(网络用语mac是什么意思)

    网络mac是什么意思(网络用语mac是什么意思)

  • 苹果7麦克风被占用(苹果麦克风被禁用了怎么开启声音)

    苹果7麦克风被占用(苹果麦克风被禁用了怎么开启声音)

  • 9600kf配什么显卡(技嘉3060ti显卡怎么样)

    9600kf配什么显卡(技嘉3060ti显卡怎么样)

  • 支付宝怎样才能得到敬业福(支付宝怎样才能接到大量异地收款)

    支付宝怎样才能得到敬业福(支付宝怎样才能接到大量异地收款)

  • jcg路由器怎么设置(jcg路由器怎么设置wifi5)

    jcg路由器怎么设置(jcg路由器怎么设置wifi5)

  • 快手怎么暂停播放(快手怎么暂停播放功能)

    快手怎么暂停播放(快手怎么暂停播放功能)

  • 苹果11pro长宽高(苹果11pro长宽高多少厘米)

    苹果11pro长宽高(苹果11pro长宽高多少厘米)

  • 库乐队一直横屏怎么办呀(库乐队横屏怎么让它竖屏呢)

    库乐队一直横屏怎么办呀(库乐队横屏怎么让它竖屏呢)

  • 域里怎么做目录(目录的域怎么弄)

    域里怎么做目录(目录的域怎么弄)

  • 虾米音乐怎么下载歌曲(虾米音乐怎么下载不了)

    虾米音乐怎么下载歌曲(虾米音乐怎么下载不了)

  • 微信如何更改启动画面(微信如何更改启动界面)

    微信如何更改启动画面(微信如何更改启动界面)

  • 苹果怎么屏蔽陌生电话(苹果怎么屏蔽陌生人短信和电话)

    苹果怎么屏蔽陌生电话(苹果怎么屏蔽陌生人短信和电话)

  • 进项税额转出需要结转吗
  • 所得税清缴时有哪些调整项
  • 生产企业出口退税流程
  • 固定资产报废收入
  • 劳务报酬和工资薪金哪个税率高
  • 房屋产权出典的由承典人作为房产税纳税义务人
  • 高温费国家有规定,一定要支付吗?
  • 出租车手撕票可以换成增值税发票吗
  • 金蝶标准版如何结转到下月
  • 离职补偿金怎么做账
  • 社保基数与工资不符
  • 用于职工住宿的会计科目
  • 贸易公司委托加工买进和卖出东西不一样
  • 电子承兑汇票如何撤回
  • 签订合同发放工资可以税前扣除吗?
  • 当月预交增值税时所属期选了上期怎么办
  • 员工替公司垫付的费用还没有报销就被开除了
  • 个体生产经营所得税税率2023
  • 变更办税人信息在哪变更
  • 税前不得扣除的项目
  • 收购全资子公司的账务处理
  • 业务宣传费企业所得税扣除标准是多少
  • 借给其他公司借款会计分录
  • 消费税税目是否含税
  • 库存商品的核算方法
  • 企业注销个税怎样更正申报呢
  • 应发工资包含罚款吗
  • 个人汇算清缴已经做了公司怎么改申报
  • macOS 11.0.1(20B29)更新了什么?macOS Big Sur 11.0.1(20B29)更新详解
  • 总分公司企业所得税合并申报
  • mmc.exe是什么进程
  • 企业自建房需要交哪些税
  • 什么票据行为是合法的
  • 新准则规定
  • 劳务报酬所得缴纳时间
  • 短期借款转长期借款利息
  • 固定资产报废后的处理程序
  • html怎么引入图片
  • 花雕典故
  • php数组有哪几种类型
  • 一般股份支付的确认计量及帐务处理怎么做?
  • 小规模纳税人附加税减免政策2023
  • 用友重新安装步骤
  • 预缴税款是什么意思
  • 专项应付款 会计分录
  • 履约保证金打到个人账户合法吗
  • 三方协议代付的钱在哪里
  • 资产减值损失科目借方增加还是减少
  • 代销产品如何做会计分录
  • 如果企业长期股票怎么办
  • 增值税运费发票清单怎么开
  • 会计中借方和贷方各指什么
  • 应付账款的逾期利息计入什么科目
  • 在外地设办事处需要办什么手续
  • 委托加工物资贷方
  • 报废车收到的钱怎么做账
  • 一般性企业
  • 报关单新版
  • 年数总和法计提折旧的例题
  • sql server233错误
  • sql server 2008教程
  • 不一样的萧先生
  • 移动u盘的作用
  • 在solaris8下
  • aow.exe是什么进程
  • win10分辨率2560*1080
  • w7系统怎么扫描
  • 安全账户管理器或本地安全机构服务器
  • perl中\s+
  • perl执行linux命令
  • js实现简单的画图功能
  • js中alert弹不出来
  • bootstrap要学吗
  • easyui combobox onchange
  • python入门100例
  • adb远程调试工具
  • jQuery ajax应用总结
  • 填了专项附加扣除但没有进行综合年度汇算
  • 四川税务局网上办事大厅
  • 个人所得税法实施条例2011
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设