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 (<div>我是子组件大名:{cname}虚岁:{cage}</div>);

};
//用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 (
    <div className="App">
      <div>数值递增: {count}</div>
      <button onClick={() => { setCount(count => count + 1); }}>点击增加</button>
      <MemoChildComp info={info}/>
    </div>
  );
};

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 (<div>我是子组件大名:{cname}虚岁:{cage}</div>);
};
//用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 (
    <div className="App">
      <div>数值递增: {count}</div>
      <button onClick={() => { setCount(count => count + 1); }}>点击增加</button>
      <MemoChildComp info={info}/>
    </div>
  );
};

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 (<div>我是子组件</div>);

};
//用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 (
    <div className="App">
      <div>数值递增: {count}</div>
      <button onClick={() => { setCount(count => count + 1); }}>点击增加</button>
      <MemoChildComp info={info} toChid={toChild}/>
    </div>
  );
};

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 (<div>我是子组件</div>);

};
//用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 (
    <div className="App">
      <div>数值递增: {count}</div>
      <button onClick={() => { setCount(count => count + 1); }}>点击增加</button>
      <MemoChildComp info={info} toChid={toChild}/>
    </div>
  );
};

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 (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

这个案例中使用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 (
    <div className="App">
      <div>数值递增: {count}</div>
      <button onClick={() => { setCount(count + 1); }}>点击增加</button>
      <button onClick={onCountClick}>点击展示</button>
    </div>
  );
};

export default Parent

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

最后:

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

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

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

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:在React中,memo是一个高阶组件,用于优化函数组件的性能。它类似于React.PureComponent,通过对内部对象进行浅比较来判断是否重新渲染组件。\[2\]useEffect是React提供的一个Hook函数,用于在函数组件中执行副作用操作。它可以在组件渲染完成后执行一些异步操作,比如发送网络请求、订阅事件等。\[3\]useMemoReact提供的另一个Hook函数,用于在函数组件中进行性能优化。它可以缓存计算结果,避免重复计算,类似于Vue中的computed属性。\[1\]useRefReact提供的一个Hook函数,用于在函数组件中创建一个可变的引用。它可以用来保存组件的状态,或者获取DOM元素的引用。\[1\]useState是React提供的一个Hook函数,用于在函数组件中创建一个可变的状态。它返回一个数组,第一个元素是当前的状态值,第二个元素是更新状态的函数。 所以,react memo用于优化函数组件的性能,useEffect用于执行副作用操作,useMemo用于缓存计算结果,useRef用于创建可变的引用,useState用于创建可变的状态。 #### 引用[.reference_title] - *1* *2* [useMemo,memo,useRef等相关hooks详解](https://blog.csdn.net/weixin_44441196/article/details/117328033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [一文轻松掌握react-hook(useState、useReducer、useEffect、useCallbackuseMemouseRef、useContext...)](https://blog.csdn.net/u010074572/article/details/105176653)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值