当前位置: 首页 > 科技观察

ReactHook四大组件优化

时间:2023-03-12 13:02:13 科技观察

ReactHook已经成为最流行的开发范式。在React16.8之后,基于Hook的开发大大简化了开发者的效率。同时,不正确的使用ReactHook也会带来很多性能问题。本文梳理了在基于ReactHook开发组件的过程中如何提升性能。每次点击Increasebeforecomponentextractionoptimization都会导致子组件Child的渲染,即使子组件没有状态变化:functionBefore(){console.log('Demo1Parent')let[count,setCount]=useState(0)让[name,setName]=useState('-')consthandleClick=()=>{setCount(count+1)}consthandleInput=(e)=>{setName(e.target.value)}return(

{count}增加
改变子组件:

)}//子组件函数Child(props){console.log('Demo1Child')return(<divclassName='l50'>Subcomponentrendering:{props.name}
)}优化后,只需要将Increase抽离成一个独立的组件,此时点击按钮,子组件就会不被渲染。/***优化后,Increase被提取出来后,上下文发生变化,在组件内部*@returns*/functionIncrease(){console.log('ChildIncrease')let[count,setCount]=useState(0)consthandleClick=()=>{setCount(count+1)}return(
{count}增加
)}functionAfter(){console.log('Demo1Parent')let[name,setName]=useState('-')consthandleInput=(e)=>{setName(e.target.value)}return(
改变子组件:
)}//子组件函数Child(props){console.log('Demo1Child')return(子组件渲染:{props.name})}备忘录优化组件也是基于上述预优化代码。即使不提取组件,使用备忘录优化后,点击按钮时也不会触发二次渲染//在优化函数AfterMemo(){console.log('Demo1Parent')let[count,setCount]=useState(0)let[name,setName]=useState('-')consthandleClick=()=>{setCount(count+1)}consthandleInput=(e)=>{setName(e.target.value)}return(
{count}增加
改变子组件:)}//子组件constChild=memo((props)=>{console.log('Demo1Child')return(子组件渲染:{props.name})})React.memo语法React.memo是一个高级组件,类似于React.PureComponentfunctionTestComponent(props){//使用props渲染}functionareEqual(prevProps,nextProps){/*IfnextProps被传递如果render方法的返回结果与传递给render方法的prevProps的返回结果相同,则返回true,否则返回false*/}exportdefaultReact.memo(TestComponent,areEqual)不同于类组件中的shouldComponentUpdate()方法,如果props相等,则areEqual返回true;如果道具不相等,它将返回false。这与shouldComponentUpdate方法的返回值相反。如果useCallback优化组件使用了memo,当遇到如下场景时,也会触发子组件渲染。比如给Child绑定一个handleClick,在子组件内部添加一个按钮,当子组件的按钮被点击时,改变count的值,即使name没有改变,也会触发渲染的子组件,为什么?memo不是只有判断名字变了才更新吗?functionBefore(){console.log('Demo1Parent')let[count,setCount]=useState(0)let[name,setName]=useState('-')consthandleClick=()=>{setCount(count+1)}consthandleInput=(e)=>{setName(e.target.value)}consthandleChange=()=>{setCount(count+1)}return(
<标签>计数器:{count}增加
更改子组件:)}//子组件constChild=memo((props)=>{console.log('Demo1Child')return(子组件渲染:{props.name}Changecount)})不是memo不生效,是因为state改变时,父组件会重新-execute,重新创建新的handleChange函数,handleChange的变化导致子组件重新渲染。优化后点击父组件的Increase按钮??改变count值。useCallback包裹了handleChange函数后,我们会发现子组件不再渲染了,说明每当父组件执行时,都没有创建新的handleChange函数,这是通过useCallback优化后的效果。即使我们点击了子组件的按钮,也不会触发子组件的渲染,计数也会累加。functionAfter(){console.log('Demo1Parent')let[count,setCount]=useState(0)lettext=useRef();让[name,setName]=useState('-')consthandleClick=()=>{setCount(count+1)}consthandleInput=(e)=>{setName(e.target.value)}consthandleChange=useCallback(()=>{//为了让count累加,我们使用ref获取valueletval=parseInt(text.current.textContent);setCount(val+1)},[])return(
{count}增加
改变子组件:)}useCallbackFunction//用法useCallback(()=>{//to-do},[])//ExamplefunctionApp(){//点击按钮调用这个函数,但是返回被缓存了constonClick=useCallback(()=>{console.log('我被缓存了,不管我怎么点击,返回的都是一样的');},[]);return(Click);}useCallback接收2个参数,第一个是缓存函数,第二个是依赖值,主要用于缓存函数,第二个time会返回相同的结果useMemo优化了我们的定义创建了一个total函数,内部用1填充100次,通过reduce计算求和。经过测试发现,点击Increase按钮??后,只会执行total1,不会执行total2。假设total的计算量巨大,会造成内存的浪费,useMemo可以帮我们缓存计算出来的值。functionBefore(){console.log('Demo1Parent')let[count,setCount]=useState(0)consthandleClick=()=>{setCount(count+1)}consttotal1=()=>{console.log('计算和1')letarr=Array.from({length:100}).fill(1)returnarr.reduce((prev,next)=>prev+next,0)}//缓存对象valueconsttotal2=useMemo(()=>{console.log('calculatesum2')letarr=Array.from({length:100}).fill(1)returnarr.reduce((prev,next)=>prev+next,0)},[count])return(
{count}增加
{total1()}{total2}
)}useMemo语法constmemoizedValue=useMemo(()=>computeExpensiveValue(a,b),[a,b]);传入一个函数,返回一个memoizedvalues,需要注意的是函数中一定要有返回值。第二个参数将取决于值。当相关值更新时,将重新计算。useCallback和useMemo的区别在于它们都用于缓存。useCallback主要用于缓存函数,返回一个缓存的函数,而useMemo主要用于缓存值,返回一个缓存的值。以上就是我这次为大家整理的集中通用的组件优化。当然合理使用useEffectuseLayout希望对不懂的朋友有用。