您现在的位置是:网站首页> 编程资料编程资料

React Hook 四种组件优化总结_React_

2023-05-24 379人已围观

简介 React Hook 四种组件优化总结_React_

前言

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 (         
                                              {count}                 Increase             
                             改变子组件:                              
                                  
    ) } // 子组件 function Child(props){     console.log('Demo1 Child')     return (                      子组件渲染:{props.name}         
    ) }

优化后

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

/**  * 优化后,Increase提取以后,上下文发生变化,组件内  * @returns   */ function Increase(){     console.log('Child Increase')     let [count,setCount] = useState(0)     const handleClick = ()=>{         setCount(count+1)     }     return (         
                                              {count}                 Increase             
        
    ) } function After(){     console.log('Demo1 Parent')     let [name,setName] = useState('-')     const handleInput = (e)=>{         setName(e.target.value)     }     return (         
                                          改变子组件:                              
                          ) } // 子组件 function Child(props){     console.log('Demo1 Child')     return (                      子组件渲染:{props.name}              ) }

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 (         
                                              {count}                 Increase             
                             改变子组件:                              
                          ) } // 子组件 const Child = memo((props)=>{     console.log('Demo1 Child')     return (                      子组件渲染:{props.name}              ) })

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 (         
                                              {count}                 Increase             
                             改变子组件:                              
                          ) } // 子组件 const Child = memo((props)=>{     console.log('Demo1 Child')     return (                      子组件渲染:{props.name}             更改count              ) })

并不是 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 (         
                                              {count}                 Increase             
                             改变子组件:                              
                          ) }

useCallback 作用

// 用法 useCallback(()=>{   // to-do },[]) // 示例 function App(){   // 点击按钮调用此函数,但返回被缓存   const onClick = useCallback(() => {     console.log('我被缓存了,怎么点击都返回一样');   }, []);   return (      点击   ); }
  • 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 (         
                                              {count}                 Increase             
            
                                 {total1()}                 {total2}             
        
    ) }

useMemo 语法

-六神源码网