React Hook 四种组件优化总结

目录
  • 前言
  • 组件抽取
  • memo 优化组件
    • React.memo 语法
  • useCallback 优化组件
    • useCallback 作用
  • useMemo 优化
    • useMemo 语法
  • useCallback 和 useMemo 区别

前言

React Hook 已成为当前最流行的开发范式,React 16.8 以后基于 Hook 开发极大简化开发者效率,同时不正确的使用 React Hook也带来了很多的性能问题,本文梳理基于 React Hook 开发组件的过程中如何提高性能。

组件抽取

优化前

每次点击 Increase 都会引起子组件 Child 的渲染,哪怕子组件并没有状态变化

function Before(){
    console.log('Demo1 Parent')
    let [count,setCount] = useState(0)
    let [name,setName] = useState('-')
    const handleClick = ()=>{
        setCount(count+1)
    }
    const handleInput = (e)=>{
        setName(e.target.value)
    }
    return (
        <div>
            <div className='l50'>
                <label>计数器:</label>
                <span className='mr10'>{count}</span>
                <button className='ml10' onClick={handleClick}>Increase</button>
            </div>
            <div className='l50'>
                <label htmlFor="">改变子组件:</label>
                <input type="text" onChange={handleInput}/>
            </div>
            <hr />
            <Child name={name}/>
        </div>
    )
}
// 子组件
function Child(props){
    console.log('Demo1 Child')
    return (
        <div className='l50'>
            子组件渲染:{props.name}
        </div>
    )
}

优化后

只需要把 Increase 抽取成独立的组件即可。此时点击按钮,子组件并不会渲染。

/**
 * 优化后,Increase提取以后,上下文发生变化,组件内
 * @returns 
 */
function Increase(){
    console.log('Child Increase')
    let [count,setCount] = useState(0)
    const handleClick = ()=>{
        setCount(count+1)
    }
    return (
        <div>
            <div className='l50'>
                <label>计数器:</label>
                <span className='mr10'>{count}</span>
                <button className='ml10' onClick={handleClick}>Increase</button>
            </div>
        </div>
    )
}
function After(){
    console.log('Demo1 Parent')
    let [name,setName] = useState('-')
    const handleInput = (e)=>{
        setName(e.target.value)
    }
    return (
        <div>
            <Increase/>
            <div className='l50'>
                <label htmlFor="">改变子组件:</label>
                <input type="text" onChange={handleInput}/>
            </div>
            <Child name={name}/>
        </div>
    )
}
// 子组件
function Child(props){
    console.log('Demo1 Child')
    return (
        <div className='l50'>
            子组件渲染:{props.name}
        </div>
    )
}

memo 优化组件

同样基于上述优化前代码,如果不抽取组件,使用 memo 优化后,当点击按钮后,也不会触发二次渲染。

// 优化前
function AfterMemo(){
    console.log('Demo1 Parent')
    let [count,setCount] = useState(0)
    let [name,setName] = useState('-')
    const handleClick = ()=>{
        setCount(count+1)
    }
    const handleInput = (e)=>{
        setName(e.target.value)
    }
    return (
        <div>
            <div className='l50'>
                <label>计数器:</label>
                <span className='mr10'>{count}</span>
                <button className='ml10' onClick={handleClick}>Increase</button>
            </div>
            <div className='l50'>
                <label htmlFor="">改变子组件:</label>
                <input type="text" onChange={handleInput}/>
            </div>
            <Child name={name}/>
        </div>
    )
}

// 子组件
const Child = memo((props)=>{
    console.log('Demo1 Child')
    return (
        <div className='l50'>
            子组件渲染:{props.name}
        </div>
    )
})

React.memo 语法

React.memo 为高阶组件,与 React.PureComponent相似。

function TestComponent(props){
  // 使用 props 渲染
}
function areEqual(prevProps,nextProps){
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
export default React.memo(TestComponent,areEqual)

与 class 组件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 会返回 true;如果 props 不相等,则返回 false。这与 shouldComponentUpdate 方法的返回值相反。

useCallback 优化组件

如果已经用了 memo ,当遇到下面这种场景时,同样会触发子组件渲染。比如,给 Child 绑定一个 handleClick ,子组件内部增加一个按钮,当点击子组件的按钮时,更改 count 值,即使没有发生 name 变化,也同样会触发子组件渲染,为什么?memo 不是会判断 name 变化了,才会更新吗?

function Before(){
    console.log('Demo1 Parent')
    let [count,setCount] = useState(0)
    let [name,setName] = useState('-')
    const handleClick = ()=>{
        setCount(count+1)
    }
    const handleInput = (e)=>{
        setName(e.target.value)
    }
    const handleChange = ()=>{
        setCount(count+1)
    }
    return (
        <div>
            <div className='l50'>
                <label>计数器:</label>
                <span className='mr10'>{count}</span>
                <button className='ml10' onClick={handleClick}>Increase</button>
            </div>
            <div className='l50'>
                <label htmlFor="">改变子组件:</label>
                <input type="text" onChange={handleInput}/>
            </div>
            <Child name={name} handleClick={handleChange}/>
        </div>
    )
}

// 子组件
const Child = memo((props)=>{
    console.log('Demo1 Child')
    return (
        <div className='l50'>
            子组件渲染:{props.name}
            <button onClick={props.handleClick}>更改count</button>
        </div>
    )
})

并不是 memo 没有生效,是因为当状态发生变化时,父组件会从新执行,导致从新创建了新的handleChange 函数,而 handleChange 的变化导致了子组件的再次渲染。

优化后

点击父组件的Increase按钮,更改了 count 值,经过 useCallback 包裹 handleChange 函数以后,我们会发现子组件不再渲染,说明每当父组件执行的时候,并没有创建新的 handleChange 函数,这就是通过 useCallback 优化后的效果。 即使我们点击子组件的按钮,也同样不会触发子组件的渲染,同样 count 会进行累加。

function After(){
    console.log('Demo1 Parent')
    let [count,setCount] = useState(0)
    let text = useRef();
    let [name,setName] = useState('-')
    const handleClick = ()=>{
        setCount(count+1)
    }
    const handleInput = (e)=>{
        setName(e.target.value)
    }
    const handleChange = useCallback(()=>{
        // 为了让 count 能够累加,我们使用ref 获取值
        let val = parseInt(text.current.textContent);
        setCount(val+1)
    },[])
    return (
        <div>
            <div className='l50'>
                <label>计数器:</label>
                <span className='mr10' ref={text}>{count}</span>
                <button className='ml10' onClick={handleClick}>Increase</button>
            </div>
            <div className='l50'>
                <label htmlFor="">改变子组件:</label>
                <input type="text" value={name} onChange={handleInput}/>
            </div>
            <Child name={name} handleClick={handleChange}/>
        </div>
    )
}

useCallback 作用

// 用法
useCallback(()=>{
  // to-do
},[])

// 示例
function App(){
  // 点击按钮调用此函数,但返回被缓存
  const onClick = useCallback(() => {
    console.log('我被缓存了,怎么点击都返回一样');
  }, []);

  return ( 
    <button onClick={onClick}>点击</button>
  );
}
  • useCallback 接收 2 个参数,第一个为缓存的函数,第二个为依赖值
  • 主要用于缓存函数,第二次会返回同样的结果。

useMemo 优化

我们定义了一个total函数,内部使用 1 填充了100次,通过 reduce 计算总和,经过测试发现点击 Increase按钮后,只会执行 total1 ,不会执行 total2,假设total计算量巨大,就会造成内存的浪费,通过 useMemo 可以帮我们缓存计算值。

function Before(){
    console.log('Demo1 Parent')
    let [count,setCount] = useState(0)
    const handleClick = ()=>{
        setCount(count+1)
    }
    const total1 = ()=>{
        console.log('计算求和1')
        let arr = Array.from({ length:100 }).fill(1)
        return arr.reduce((prev,next)=>prev+next,0)
    }
    // 缓存对象值
    const total2 = useMemo(()=>{
        console.log('计算求和2')
        let arr = Array.from({ length:100 }).fill(1)
        return arr.reduce((prev,next)=>prev+next,0)
    },[count])
    return (
        <div>
            <div className='l50'>
                <label>计数器:</label>
                <span className='mr10'>{count}</span>
                <button className='ml10' onClick={handleClick}>Increase</button>
            </div>
            <div>
                <label>总和:</label>
                <span>{total1()}</span>
                <span>{total2}</span>
            </div>
        </div>
    )
}

useMemo 语法

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • 传入一个函数进去,会返回一个 memoized 值,需要注意的是,函数内必须有返回值
  • 第二个参数会依赖值,当依赖值更新时,会从新计算。

useCallback 和 useMemo 区别

他们都用于缓存,useCallback 主要用于缓存函数,返回一个 缓存后 函数,而 useMemo 主要用于缓存值,返回一个缓存后的值。

到此这篇关于React Hook 四种组件优化总结的文章就介绍到这了,更多相关React Hook 组件优化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于react hooks,zarm组件库配置开发h5表单页面的实例代码

    最近使用React Hooks结合zarm组件库,基于js对象配置方式开发了大量的h5表单页面.大家都知道h5表单功能无非就是表单数据的收集,验证,提交,回显编辑,通常排列方式也是自上向下一行一列的方式显示 , 所以一开始就考虑封装一个配置化的页面生成方案,目前已经有多个项目基于此方式配置开发上线,思路和实现分享一下. 使用场景 任意包含表单的h5页面(使用zarm库,或自行适配自己的库) 目标 代码实现简单和简洁 基于配置 新手上手快,无学习成本 老手易扩展和维护 写之前参考了市面上的一些方案

  • ReactHooks+ts(函数组件)实现原生轮播的示例

    目录 1.下载依赖(第一个是js依赖,第二个是ts依赖) 2.创建tsx文件 3.创建less文件 1.下载依赖(第一个是js依赖,第二个是ts依赖) npm install smoothscroll-polyfill --save npm i --save-dev @types/smoothscroll-polyfill 2.创建tsx文件 import React, { useRef, useState, useEffect } from 'react' import './index.le

  • React 使用Hooks简化受控组件的状态绑定

    开始之前 阅读本文需要对以下几项有一定了解 ECMAScript 6 文章中大量用到了 ES6 语法,比如解构赋值和函数参数默认值.剩余参数.展开语法.箭头函数等. Hooks React 在 16.8 版本中推出了 Hooks,它允许你在"函数组件"中使用"类组件"的一些特性. React 本身提供了一些 Hooks,比如 useState.useReducer 等.通过在一个以"use"作为命名起始的函数中调用这些 Hooks,就得到了一个

  • 使用 React Hooks 重构类组件的示例详解

    目录 1. 管理和更新组件状态 2. 状态更新后的操作 3. 获取数据 4. 卸载组件时清理副作用 5.  防止组件重新渲染 6. Context API 7. 跨重新渲染保留值 8. 如何向父组件传递状态和方法? 9. 小结 最初,在 React 中可以使用 createClass 来创建组件,后来被类组件所取代.在 React 16.8 版本中,新增的 Hooks 功能彻底改变了我们编写 React 程序的方式,使用 Hooks 可以编写更简洁.更清晰的代码,并为创建可重用的有状态逻辑提供了

  • 进入Hooks时代写出高质量react及vue组件详解

    目录 概述 1.组件什么时候拆?怎么拆? 2.如何组织拆分出的组件文件? 3.如何用hooks抽离组件逻辑? 题外话:全局状态的管理 概述 vue和react都已经全面进入了hooks时代(在vue中也称为组合式api,为了方便后面统一称为hooks),然而受到以前react中类组件和vue2写法的影响,很多开发者都不能及时转换过来,以致于开发出一堆面条式代码,整体的代码质量反而不如改版以前了. hooks组件到底应该如何写,我也曾为此迷惘过一段时间.特别我以前以react开发居多,但在转到新岗

  • 详解React hooks组件通信方法

    目录 一.前言 二.父子组件通信 1)父组件传值给子组件 2)子组件传值给父组件 3)跨组件传值(父传孙子组件) 一.前言 组件通信是React中的一个重要的知识点,下面列举一下 react hooks中常用的父子.跨组件通信的方法 二.父子组件通信 1)父组件传值给子组件 子组件代码: //子组件 const Child = ({ param1, param2 }) => { return <>父组件传递的参数:{param1},{param2}</> } param1.p

  • 详解react hooks组件间的传值方式(使用ts)

    目录 父传子 子传父 跨级组件(父传后代) 父传子 通过props传值,使用useState来控制state的状态值 父组件 Father.tsx里: 子组件 Child.tsx里: 展示效果: 子传父 跟react的方式一样,像子组件传入回调函数,通过接收子组件的返回值,再去更新父组件的state 父组件,Father.tsx里: 子组件,Child.tsx里: 展示效果: 子传父优化版,使用useCallback存放处理事件的函数 父组件,Father.tsx里: 子组件,Child.tsx

  • 使用hooks写React组件需要注意的5个地方

    Hook是React16.8开始新增的特性.虽然React官方文档已经作出了针对React hooks的相关概念的讲解,但是光看官方文档是很难将hooks使用好的,在编写hooks的过程中很容易跳进陷阱和错误.本文总结了5个不好的地方. 01.不需要render的场景下使用useState 在函数组件中我们可以使用useState来管理状态,这使得对状态的管理变得很简单,但是也容易被滥用,我们通过下面的代码样例看下容易忽略的地方. 不推荐× function ClickButton(props)

  • React函数组件hook原理及构建hook链表算法详情

    目录 写在前面的小结 Demo fiber hook 链表 hook 对象及其属性介绍 useState Hook 对象 useRef Hook useEffect.useLayoutEffect 以及 useImperativeHandle useMemo useCallback 构建 Hook 链表的源码 renderWithHooks 函数组件执行 构建 hook 链表的算法 mountWorkInProgressHook 构建 hook 链表算法 updateWorkInProgress

  • React Hook 四种组件优化总结

    目录 前言 组件抽取 memo 优化组件 React.memo 语法 useCallback 优化组件 useCallback 作用 useMemo 优化 useMemo 语法 useCallback 和 useMemo 区别 前言 React Hook 已成为当前最流行的开发范式,React 16.8 以后基于 Hook 开发极大简化开发者效率,同时不正确的使用 React Hook也带来了很多的性能问题,本文梳理基于 React Hook 开发组件的过程中如何提高性能. 组件抽取 优化前 每

  • React Hook实现对话框组件

    React Hook实现对话框组件,供大家参考,具体内容如下 准备 思路:对话框组件是有需要的时候希望它能够弹出来,不需要的时候在页面上是没有任何显示的,这就意味着需要一个状态,在父组件点击按钮或其他的时候能够更改+ 它并显示,同时子组件(对话框组件)点击关闭的时候也需要更改这个状态.     进阶:为对话框组件背景添加蒙皮,当对话框内容需要滚动时限制浏览器页面的滚动. React Hook 主要用到了 useState,useContext,useContext 主要用于将父组件的 setSt

  • react四种组件中DOM样式设置方式详解

    1.行内样式 想给虚拟dom添加行内样式,需要使用表达式传入样式对象的方式来实现 行内样式需要写入一个样式对象,而这个样式对象的位置可以放在很多地方 例如:render函数里.组件原型上.外链js文件中 注意:这里的两个括号,第一个表示我们在要JSX里插入JS了,第二个是对象的括号 <p style={{color:'red', fontSize:'14px'}}>Hello world</p> 2.使用class React推荐我们使用行内样式,因为React觉得每一个组件都是一

  • angular 动态组件类型详解(四种组件类型)

    组件类型1:纯函数功能,而没有视图部分,即Factory(类似于$http) promise.component.html 常见的有内置的$http,$q之类的.一般使用promise与作用域进行交互 组件类型2:不是常驻于视图,而是动态插入的.有UI的一类组件,有输入交互.不常被调用(类似于Model对话框) factory.component.html 并发性.这里收到es6的启发.在factory内使用了构造函数,来区分不同的实例.当然,factory接口返回的类型要根据需求来定:仅仅是一

  • Vue自定义组件的四种方式示例详解

    四种组件定义方式都存在以下共性(血泪史) 规则: 1.组件只能有一个根标签 2.记住两个词全局和局部 3.组件名称命名中'-小写字母'相当于大写英文字母(hello-com 相当于 helloCom) 而对于在HTML中自定义组件的时候有4种写法,不过也只是殊途同归,都是用template属性对应的只有一个根标签的HTML代码. 1.全局组件 定义方式示例: Vue.component("hello-component",{ props:["message"], t

  • React Hook的使用示例

    这篇文章分享两个使用React Hook以及函数式组件开发的简单示例. 一个简单的组件案例 Button组件应该算是最简单的常用基础组件了吧.我们开发组件的时候期望它的基础样式能有一定程度的变化,这样就可以适用于不同场景了.第二点是我在之前做项目的时候写一个函数组件,但这个函数组件会写的很死板,也就是上面没有办法再绑定基本方法.即我只能写入我已有的方法,或者特性.希望编写Button组件,即使没有写onClick方法,我也希望能够使用那些自带的默认基本方法. 对于第一点,我们针对不同的class

  • React中10种Hook的使用介绍

    目录 React Hook是什么? React目前提供的Hook 1.useState 2.useEffect & useLayoutEffect 3.useMemo & useCallback 4.useRef 5.useContext 6.useReducer React Hook是什么? React官网是这么介绍的: Hook 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 完全可选的 你无需重写任何已有

  • React Hook父组件如何获取子组件的数据/函数

    目录 React Hook父组件获取子组件数据/函数 子组件MyWorldMap 父组件MyTrip React Hook父组件提交子组件form 父组件 子组件 React Hook父组件获取子组件数据/函数 我们知道在react中,常用props实现子组件数据到父组件的传递,但是父组件调用子组件的功能却不常用. 文档上说ref其实不是最佳的选择,但是想着偷懒不学redux,在网上找了很多教程,要不就是hook的讲的太少,要不就是父子组件傻傻分不清,于是只好再啃了一下文档,就学了一下其它hoo

  • Vue项目中打包优化的四种方法详解

    目录 前言 打包优化的目的: 性能优化的主要方向: 1.异步组件配置(路由懒加载) 2.去掉打包后的 console 3.使用CDN 4.yarn build生成dist目录 总结 前言 默认情况下,通过import语法导入的第三方依赖包,最终会全部打包到一个js文件中,会导致单文件体积过大大,在网速底下时会阻塞网页加载,影响用户体验. 打包优化的目的: 1.项目启动速度,和性能 2.必要的清理数据 3.减少打包后的体积 第一点是核心,第二点呢其实主要是清理console 性能优化的主要方向:

  • 详解react关于事件绑定this的四种方式

    在react组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前组件,而且react还会对这种引用进行缓存,以达到cpu和内存的最大化.在使用了es6 class或者纯函数时,这种自动绑定就不复存在了,我们需要手动实现this的绑定 React事件绑定类似于DOM事件绑定,区别如下: 1.React事件的用驼峰法命名,DOM事件事件命名是小写 2.通过jsx,传递一个函数作为event handler,而不是一个字符串. 3.React事件不能通过返回false来阻止默认事件,

随机推荐