React Hook 四种组件优化总结

 更新时间:2022年07月27日 10:21:15   作者:​ 河畔一角​  
这篇文章主要介绍了React Hook四种组件优化总结,文章围绕主题展开详细的内容介绍,具有一定的参考价孩子,需要的朋友可以参考一下

前言

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 组件优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React实现pc端的弹出框效果

    React实现pc端的弹出框效果

    这篇文章主要为大家详细介绍了React实现pc端的弹出框效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • React数据传递之组件内部通信的方法

    React数据传递之组件内部通信的方法

    这篇文章主要介绍了React数据传递之组件内部通信的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • 解决React报错`value` prop on `input` should not be null

    解决React报错`value` prop on `input` should&

    这篇文章主要为大家介绍了React报错`value` prop on `input` should not be null解决方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • react项目使用redux初始化方式

    react项目使用redux初始化方式

    这篇文章主要介绍了react项目使用redux初始化方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 一看就懂的ReactJs基础入门教程-精华版

    一看就懂的ReactJs基础入门教程-精华版

    现在最热门的前端框架有AngularJS、React、Bootstrap等。自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领略ReactJs的风采吧~~ 文章有点长,耐心读完,你会有很大收获哦
    2021-04-04
  • React虚拟列表的实现代码

    React虚拟列表的实现代码

    最近看了vueuse的useVirtualList的实现方式,发现虚拟滚动效果不错,就尝试着同样的写法改成react版本,虚拟列表主要包含三部分组成,offset,viewcapacity,overscan,本文就给大家介绍一下React虚拟列表的实现,需要的朋友可以参考下
    2023-08-08
  • 详解React组件卸载怎么中止递归方法

    详解React组件卸载怎么中止递归方法

    最近在处理项目代码的时候,出现了一个bug,组件中的方法在组件卸载后仍然在执行,代码片段发给我看,但是变量的用意我也不懂,只看到有方法调用自身方法,这不就是递归嘛,所以本文详细给大家介绍了React组件卸载怎么中止递归方法,需要的朋友可以参考下
    2024-01-01
  • React的createElement和render手写实现示例

    React的createElement和render手写实现示例

    这篇文章主要为大家介绍了React的createElement和render手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 详解React中的this指向

    详解React中的this指向

    这篇文章主要介绍了React中的this指向的相关资料,帮助大家更好的理解和学习使用React,感兴趣的朋友可以了解下
    2021-04-04
  • react实现换肤功能的示例代码

    react实现换肤功能的示例代码

    这篇文章主要介绍了react实现换肤功能的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08

最新评论