react 中 memo、useCallback、useMemo使用场景

88 阅读3分钟

在父组件中嵌套子组件 父组件更新 导致子组件也重新渲染

父组件

在父组件中嵌套了子组件 子组件的作用也很单纯 就只有渲染 一个 h3 标题
 import { useState } from 'react';
 import Son from './Son';
 ​
 export default function Father() {
   const [count, setCount] = useState(0);
   
   return (
     <>
       <div>
         <h2>父组件</h2>
         <h2>{count}</h2>
         <button onClick={() => setCount(count + 1)}>+1</button>
         <hr />
         <Son />
       </div>
     </>
   );
 }
 ​

子组件

 export default function Son() {
   console.log('render...');
   
   return (
     <>
       <div>
         <h3>子组件</h3>
       </div>
     </>
   );
 }
当在父组件中点击累加的时候 父组件重新渲染 导致了子组件也跟着重新渲染 这样对性能的损耗是很大的 当子组件中逻辑较多的时候 父组件每当重新渲染 子组件中的逻辑都会跟着重新执行

pic 00_00_00-00_00_30-16748968225991.gif

memo的使用

使用 memo 对其进行优化

子组件

 import { memo } from 'react';
 ​
 export default memo(() => {
   console.log('render...');
   
   return (
     <>
       <div>
         <h3>子组件</h3>
       </div>
     </>
   );
 });
 ​

此时我们发现 子组件在使用 memo 包裹之后 子组件的内容不会跟着重新渲染了

memo 00_00_00-00_00_30.gif

需求:在子组件中 处理父组件累加的事件

父组件

 import { useState } from 'react';
 import Son from './Son';
 ​
 export default function Father() {
   const [count, setCount] = useState(0);
 ​
   // 在父组件中写好累加的函数 将这个函数传递给子组件 子组件调用就好了
   const handleCount = () => setCount(count + 1);
   
   return (
     <>
       <div>
         <h2>父组件</h2>
         <h2>{count}</h2>
         <hr />
         <Son handleCount={handleCount} />
       </div>
     </>
   );
 }
 ​

子组件

 import { memo } from 'react';
 ​
 type TProps = {
   handleCount: () => void;
 };
 ​
 export default memo(({ handleCount }: TProps) => {
   console.log('render...');
<img src="   " alt="" width="50%" />
   return (
     <>
       <div>
         <h3>子组件</h3>
         {/* 子组件直接调用父组件传递的累加函数 */}
         <button onClick={() => handleCount()}>click</button>
       </div>
     </>
   );
 });
 ​

又发现了新的问题 memo 失效了 ???

pic2 00_00_00-00_00_30.gif

useCallback的使用

在写 handleCount 函数时需要注意的点 函数 函数 我们可能会写成下面这样

 // 错误写法
 const handleCount = () = useCallback(() => setCount(count + 1), []);
 // 正确写法
 const handleCount = useCallback(() => setCount(count + 1), []);
 ​
 // [] 数组为空表示 不检测数据更新
 const xxx = useCallback(() => {}, [])

父组件

 import { useCallback, useState } from 'react';
 import Son from './Son';
 ​
 export default function Father() {
   const [count, setCount] = useState(0);
 ​
   const handleCount = useCallback(() => setCount(count + 1), []);
   
   return (
     <>
       <div>
         <h2>父组件</h2>
         <h2>{count}</h2>
         <hr />
         <Son handleCount={handleCount} />
       </div>
     </>
   );
 }
 ​
这个时候会发现一个问题 count 的值到1就加不动了
解决方式 setCount(count + 1) --> setCount((count) => count + 1) 不断使用新值覆盖旧值

父组件

 import { useCallback, useState } from 'react';
 import Son from './Son';
 ​
 export default function Father() {
   const [count, setCount] = useState(0);
 ​
   const handleCount = useCallback(() => setCount((count) => count + 1), []);
   
   return (
     <>
       <div>
         <h2>父组件</h2>
         <h2>{count}</h2>
         <hr />
         <Son handleCount={handleCount} />
       </div>
     </>
   );
 }
 ​

在使用 useCallback 后 子组件不会跟着重新渲染了 控制台没有重复打印 render

useCallback 00_00_00-00_00_30.gif

父组件传递数据给组件时

父组件

 import { useCallback, useState } from 'react';
 import Son from './Son';
 ​
 export default function Father() {
   const [count, setCount] = useState(0);
 ​
   const obj = {
     name: '张三',
     age: 20,
   };
   
   const handleCount = useCallback(() => setCount((count) => count + 1), []);
   
   return (
     <>
       <div>
         <h2>父组件</h2>
         <h2>{count}</h2>
         <hr />
         <Son handleCount={handleCount} obj={obj} />
       </div>
     </>
   );
 }
 ​

子组件

 import { memo } from 'react';
 ​
 type TProps = {
   handleCount: () => void;
   obj: {
     name: string;
     age: number;
   };
 };
 ​
 export default memo(({ handleCount, obj }: TProps) => {
   console.log('render...');
   
   return (
     <>
       <div>
         <h3>子组件</h3>
         {/* 子组件直接调用父组件传递的累加函数 */}
         <button onClick={() => handleCount()}>click</button>
         <hr />
         <h3>
           父组件传递的值为:{obj.name} --- {obj.age}
         </h3>
       </div>
     </>
   );
 });
 ​
此时又发现新的问题了 传递数据时 子组件也跟着重新渲染了 

pic3 00_00_00-00_00_30.gif

useMemo的使用

父组件

 import { useCallback, useMemo, useState } from 'react';
 import Son from './Son';
 ​
 export default function Father() {
   const [count, setCount] = useState(0);
 ​
   const obj = {
     name: '张三',
     age: 20,
   };
 ​
   const handleCount = useCallback(() => setCount((count) => count + 1), []);
 ​
   const sendObj = useMemo(() => {
     return {
       ...obj,
     };
   }, []);
 ​
   return (
     <>
       <div>
         <h2>父组件</h2>
         <h2>{count}</h2>
         <hr />
         <Son handleCount={handleCount} obj={sendObj} />
       </div>
     </>
   );
 }
 ​

在使用了 useMemo 将数据包裹后 传递给子组件 控制台没有重新渲染 重复打印 render 了

useMemo 00_00_00-00_00_30.gif