YanTianFeng的知识库

Want Coding

Want Reading

文章 89

访问 18443

评论 2

头像

YanTianFeng

发私信

文章 89
访问 18443
评论 2
Technology and Code
返回顶部

Knowledge  useEffect, useMemo, useCallback

标签   useMemo  

  ( 7 )       ( 0 )


useEffect , useMemo , useCallback

useEffect 、 useMemo 、 useCallback 对于回调,对于数据,对于函数 useEffect 、 useMemo 、 useCallback 都是自带闭包的。也就是说,每一次组件的渲染,其都会捕获当前组件函数上下文中的状态 (state , props) ,所以每一次这三种 hooks 的执行,反映的也都是当前的状态,你无法使用它们来捕获上一次的状态。对于这种情况,我们应该使用 ref 来访问

回顾

在介绍一下这两个 hooks 的作用之前,我们先来回顾一下 react 中的性能优化。在 hooks 诞生之前,如果组件包含内部 state ,我们都是基于 class 的形式来创建组件。当时我们也知道, react 中,性能的优化点在于

1. 调用 setState ,就会触发组件的重新渲染,无论前后的 state 是否不同

2. 父组件更新,子组件也会自动的更新

基于上面的两点,我们通常的解决方案是:使用 immutable 进行比较,在不相等的时候调用 setState ;在 shouldComponentUpdate 中判断前后的 props 和 state ,如果没有变化,则返回 false 来阻止更新。 在 hooks 出来之后,我们能够使用 function 的形式来创建包含内部 state 的组件。但是,使用 function 的形式,失去了上面的 shouldComponentUpdate ,我们无法通过判断前后状态来决定是否更新。而且,在函数组件中, react 不再区分 mount 和 update 两个状态,这意味着函数组件的每一次调用都会执行其内部的所有逻辑,那么会带来较大的性能损耗。因此 useMemo 和 useCallback 就是解决性能问题的杀手锏

对比

useMemo 和 useCallback , useEffect 最大的区别是 useEffect 会用于处理副作用,而前两个 hooks 不能

useMemo 和 useCallback 都会在组件第一次渲染的时候执行。

之后会在其依赖的变量发生改变时再次执行;

并且这两个 hooks 都返回缓存的值, useMemo 返回缓存的变量, useCallback 返回缓存的函数

useEffect

使用场景: useEffect 副作用,使函数组件拥有了类似 react 的声明周期。 useEffect 会在组件每次 render 之后调用, useEffect 有两个参数,第一个为执行函数,第二个为数组[]

如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount , componentDidUpdate 和 componentWillUnmount 这三个函数的组合

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

function Example() {
  const [count, setCount] = useState(0);
  const [dataSources, setDataSources] = useState([]);

  /* 
   * 情况一:useEffect无第二个参数 
   */
  //组件初始化和render后,都会执行该useEffect
  useEffect(() => {
    console.log("相当于生命周期:componentDidMount+componentDidUpdate")
  });

  /* 
   * 情况二:useEffect有第二个参数 
   */
  //第二个参数为空数组时:组件初始化才执行
  useEffect(() => { 
    console.log("相当于生命周期:componentDidMount"); 
  }, []);
  
  //第二个参数为指定状态值时:组件初始化时和dataSources发生变化才执行
  useEffect(() => { 
    console.log("相当于生命周期:componentDidMount")
    console.log("相当于依赖dataSources状态值的生命周期:componentDidUpdate")
  }, [dataSources]);

  //执行函数内return一个函数:初始化时执行函数体,组件卸载unmount时执行return后的函数
  useEffect(() => {
    console.log("相当于生命周期:componentDidMount")
    // 执行函数中直接使用return返回一个函数,这个函数会在组件unmount时执行。
    return () => {
      console.log('相当于声明周期:componentWillUnmount');  
    }
  }, []);

  return (
    

You clicked {count} times

); } export default Example;

官方提示:与 componentDidMount 或 componentDidUpdate 不同,** useEffect 是异步的,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。**官方建议尽可能使用 useEffect , effect 不需要同步地执行 在个别情况下(例如测量布局,页面状态值闪烁 bug 时),才使用 useLayoutEffect 代替 useEffect , 形成同步,在 DOM 更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制

useMemo

当[]依赖为空数组时候,可以初始化数据。

当[ count ]不为空时,每次 count 改变都会重新计算

const initGoods = useMemo(
    () =>
      pageDetail.goods.map(item => {
        const { termList = [], isOpenTrainingCamp } = item;
        if (isOpenTrainingCamp && termList.length !== 0) {
          return {
            ...item,
            hasTerm: true,
            skuBizNo: termList[0].termId,
            termDate: termList[0].beginTime,
          };
        }
        return item;
      }),
    [],
  );

const [goods, setGoods] = useState(initGoods);

useCallback

讲完了 useMemo ,接下来是 useCallback 。 useCallback 跟 useMemo 比较类似,但它返回的是缓存的函数。 const fnA = useCallback(fnB , [ a ] )

import React, { useState, useCallback } from 'react';
 
const set = new Set();
 
export default function Callback() {
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');
 
    const callback = useCallback(() => {
        console.log(count);
    }, [count]);
    set.add(callback);
 
 
    return 

{count}

{set.size}

setVal(event.target.value)}/>
; }

每次修改 count , set.size 就会+ 1 ,这说明 useCallback 依赖变量 count , count 变更时会返回新的函数;而 val 变更时, set.size 不会变,说明返回的是缓存的旧版本函数。

使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为 props ;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助 useCallback 来返回函数,然后把这个函数作为 props 传递给子组件;这样,子组件就能避免不必要的更新。

import React, { useState, useCallback, useEffect } from 'react';
function Parent() {
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');
 
    const callback = useCallback(() => {
        return count;
    }, [count]);
    return 

{count}

setVal(event.target.value)}/>
; } function Child({ callback }) { const [count, setCount] = useState(() => callback()); useEffect(() => { setCount(callback()); }, [callback]); return
{count}
}