react hook函数组件之 useMemo、useCallback 、useRef

要想学习useMemo、useCallback 不要忽略了React.memo()这个高阶函数/组件。

React.memo()只用于函数组件,它有一个参数,可为函数也可以为组件,它的功能类似于shouldcomponent对组件接受的 props 属性进行浅比较来判断组件要不要进行重新渲染。

memo的作用:

当父组件数据变化时,代码会重新执行一遍,但是子组件数据没有变化也会跟随执行,这个时候可以使用memo将子组件封装起来,让子组件的数据只在发生改变时才会执行,节约子组件渲染的性能开销,这是memo的作用。

一定要注意区分,React.memo()、useMemo、useCallback、useRef都是做性能优化的!

React.memo()是在组件之间不存在通信时做缓存组件的用途。

useMemo,useCallback,useRef是用作组件间通信时缓存变量,函数的用途。

简单来说 useMemo(用作缓存一个值),useCallback(用来缓存一个函数)者都是避免子组件被重复渲染

案例:

import React, { memo, useState } from 'react';

// 子组件
const ChildComp = (props:{info:{cname, cage}}) => {
  console.log('子组件渲染==>',cname,cage);

  return (
我是子组件大名:{cname}虚岁:{cage}
); }; //用memo包裹子组件 const MemoChildComp = memo(ChildComp); // 父组件 const Parent = () => { const [count, setCount] = useState(0); const [cname, setCname] = useState('acccc'); const [cage, setCage] = useState(22); const info = { cname, cage}; return (
数值递增: {count}
); }; export default Parent;

问题:

点击增加按钮,count 变化,父组件会重新渲染,而子组件MemoChildComp中的 const info = { name, age }也会重新生成对象,引用地址被改变,会重新渲染子组件

解决方法:

  使用 useMemo 将对象属性包一层。useMemo 有两个参数:

  • 第一个参数是个函数,返回的对象指向同一个引用不会创建新对象
  • 第二个参数是个数组,只有数组中的变量改变时,第一个参数的函数才会返回一个新的对象。
import React, { memo, useMemo, useState } from 'react';

// 子组件
const ChildComp = (info:{info:{cname, cage}}) => {
    console.log('子组件渲染==>',cname,cage);

  return (
我是子组件大名:{cname}虚岁:{cage}
); }; //用memo包裹子组件 const MemoChildComp = memo(ChildComp); // 父组件 const Parent = () => { const [count, setCount] = useState(0); const [cname, setCname] = useState('acccc'); const [cage, setCage] = useState(22); // 使用 useMemo 将对象属性包一层 const info = useMemo(() => ({ cname, cage}), [cname, cage]); return (
数值递增: {count}
); }; export default Parent;

其次使用useMemo需要注意的点:

useMemo 的函数参数里不能写入渲染页面的逻辑,否则会造成页面死循环.

若真要写渲染页面的逻辑请使用useEffect这个函数钩子

他们的执行的时间点不一样,useMemo 是在渲染页面期间执行,而useEffect是在渲染页面完成后执行的副作用函数.

官方有明确的解释:

请记住,传递给的函数useMemo在渲染期间运行。不要在那里做任何你在渲染时通常不会做的事情。例如,副作用属于useEffect,而不是useMemo。

对二者执行时机不同解释说明的一个非常棒的案例:useMemo和useEffect有什么区别?怎么使用useMemo - 简书

其次useMemo 与useEffect这两者的第二个参数为空数组时,useMemo是不会执行,而useEffect是组件初始加载执行一次.


案例:

import React, { memo, useState,useMemo } from 'react';

// 子组件
const ChildComp = (props) => {
  console.log('子组件渲染==>',props);

  return (
我是子组件
); }; //用memo包裹子组件 const MemoChildComp = memo(ChildComp); // 父组件 const Parent = () => { const [count, setCount] = useState(0); const [cname, setCname] = useState('acccc'); const [cage, setCage] = useState(22); const info = { cname, cage}; // 使用 useMemo 将对象属性包一层 const info = useMemo(() => ({ cname, cage}), [cname, cage]); const toChild=()=>{ console.log('去子组件') } return (
数值递增: {count}
); }; export default Parent;

问题:

点击增加按钮,count 变化,父组件会重新渲染,而子组件MemoChildComp中的 const toChild=()=>{
  console.log('去子组件')}也会重新生成函数,引用地址被改变,会重新渲染子组件

解决方法:

 使用 useCallback 将函数参数包一层,将这个函数参数缓存起来。useCallback 有两个参数:

  • 第一个参数是个函数,返回的函数指向同一个引用,不会创建新函数;
  • 第二个参数是个数组,第二个参数传入一个数组,数组中的每一项一旦值或者引用发生改变,就会重新返回一个新的记忆函数提供给后面进行渲染。与useMemo类似如果是一个空数组则是无论什么情况下该函数都不会发生改变

import React, { memo,useCallback, useState,useMemo } from 'react';

// 子组件
const ChildComp = (props) => {
  console.log('子组件渲染==>',props);

  return (
我是子组件
); }; //用memo包裹子组件 const MemoChildComp = memo(ChildComp); // 父组件 const Parent = () => { const [count, setCount] = useState(0); const [cname, setCname] = useState('acccc'); const [cage, setCage] = useState(22); const info = { cname, cage}; // 使用 useMemo 将对象属性包一层 const info = useMemo(() => ({ cname, cage}), [cname, cage]); const toChild=useCallback(()=>{ console.log('去子组件') },[]) return (
数值递增: {count}
); }; export default Parent;

用useCallback包裹toChid函数后,父组件重渲染时,包裹后的函数因为依赖项不变,所以还是用记忆函数,则MemoChildComp组件中toChid函数参数并没有发生改变,子组件props也没有改变,也就不会进行重渲染。


useRef:

useRef能返回一个可变 ref 对象,其.current属性初始化为传递的参数 ( initialValue)。返回的对象将在组件的整个生命周期内持续存在。

常用作表示一个dom元素

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      
      
    
  );
}

这个案例中使用useref获取input的标签,其.current就表示当前input的dom元素,可以直接使用input对应的标签属性来做调用

其次:useRef 不仅仅是用来管理 DOM ref 的,它还相当于 this , 可以存放任何变量.

案例:实时获取当前的变量值

import React, { useRef, useState,useEffect} from 'react';

const Parent = () => {
  const [count, setCount] = useState(0);
  const latestCount = useRef(count);
  
  useEffect ({
    //每次count更新值时组件重渲染执行将count的值赋给latestCount.current
    latestCount.current=count;
  })
fucntion onCountClick(){
    setTimeOut(()=>{
        console.log('当前的count值为====>'+latestCount.current)    
    
    },3000)
}

  return (
    
数值递增: {count}
); }; export default Parent

因为 useRef 每次都会返回同一个引用, 所以在 useEffect 中修改的时候 ,在 setTimeOut中也会同时被修改. 这样子, 点击的时候就可以弹出实时的 count 了.

最后:

useMemo 和useEffect用法相似,useMemo 常用作缓存值,在组件渲染期间执行,

其次useCallback常用作缓存一个函数,与useMemo都是用作函数组件约束渲染的钩子函数,

useRef常用作表示一个DOM元素以及一个值,而.current 的作用更像是类组件的this.

你可能感兴趣的:(reactjs)