浅谈useMemo、useCallbac的原理及实现

        在我理解useMemo和useCallback是负责缓存优化的。
        我最开始讲到react组件有个机制:当父组件任何变动的情况下,子组件都会被重新渲染,即使prop所依赖的值没有发生变化。
        在类组件中,我们通常在shouldComponentUpdate中进行判断是否更新。但是在函数组件中没有shouldComponentUpdate。这意味着函数组件的每一次调用都会执行其内部的所有逻辑,那么会带来较大的性能损耗。因此useMemo 和useCallback就是解决性能问题的杀手锏。   

 useMemo用法:

        const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
        我们通过一个简单的例子来看一下:

import { useState, useMemo } from 'react';

export default function Parent() {
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');

    return (
        <div>
            <Child count={count} />
            <h4>父组件:{count}</h4>
            <div>
                <button onClick={() => setCount(count + 1)}>+</button>
                <input value={val} onChange={event => setVal(event.target.value)} />
            </div>
        </div>
    )
}

function Child({ coun t }: {count: any}) {
    // 未使用useMemo
    const time = () => {
      // 这里假设执行了一堆代码
        console.log('执行了一大堆代码');
        return new Date().getTime()
    }

    // 使用useMemo时
    // const time = useMemo(()=>{
    //     console.log('执行了一大堆代码');
    //     return new Date().getTime()
    // },[count])

    return (
        <div>
            <h4>子组件:{count}</h4>
            <p>{time()}</p>
            <hr />
        </div>
    )
}

        正常情况下,点击按钮和在输入框输入时,都会重新渲染子组件。
        当我们想只在点击按钮时渲染子组件,输入框输入时不渲染子组件,我们就可以使用useMemo,并添加上相应的依赖就可以了。
        接下来我们再实现一个简单的useMemo:

let hookStates: any = [];
let hookIndex: number = 0;

function useMemos(nextCreate: () => any, dependencies: any[]) {
    if (hookStates[hookIndex]) {   // 说明不是第一次渲染
        let [lastMemo, lastDependencies] = hookStates[hookIndex];
        let same = dependencies.every((item, index) => item === lastDependencies);
        if (same) {
            hookIndex++;
            return lastMemo;
        }
        }
        const nextValue = nextCreate();  // 此处有点不用
        // 第一次渲染 或者 不是第一次但是依赖项相同,都返回新的
        hookStates[hookIndex++] = [nextValue, dependencies];
        return nextValue;
    }

useCallback

        useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值。二者的区别是useMemo返回缓存的变量,useCallback返回缓存的函数。
        用法:useCallback(fn, deps) === useMemo(() => fn, deps)
        上面的useCallback会将我们传递给它的函数fnB返回,并且将这个结果缓存;当依赖a变更时,会返回新的函数。
        我们通过举个例子可以看一下。我们可以借助Set来检验一下返回的时候是一个新函数。

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

const set = new Set();

export default function Parent() {
    const [count, setCount] = useState(1);
    const [val, setVal] = useState('');

    // count改变,会返回一个新的函数
    // val改变,会返回之前缓存的函数
    const callback = useCallback(() => {
        return new Date().getTime();
       console.log('假设执行了一大堆代码');
    }, [count]);

    // const callback = () => {
    //     return new Date().getTime();
   //     console.log('假设执行了一大堆代码');
    // }
    // 借助set来判断返回的函数是否为新函数
    set.add(callback);

    return <div>
        <h4>父组件:{count}</h4>
        <h4>set:{set.size}</h4>
        <Child callback={callback} />
        <div>
            <button onClick={() => setCount(count + 1)}>+</button>
            <input value={val} onChange={event => setVal(event.target.value)} />
        </div>
    </div>;
}

function Child({ callback }:{callback: any}) {
    const [count, setCount] = useState(() => callback());
    useEffect(() => {
        setCount(callback());
    }, [callback]);
    return <div>
        子组件:{count}
    </div>
}

        我们再实现一个简单的useCallback

let hookStates: any = [];
let hookIndex: number = 0;
function useCallbacks(callback: any, dependencies: any) {
  if (hookStates[hookIndex]) {   // 说明不是第一次渲染
    let [lastCallback, lastDependencies] = hookStates[hookIndex];
    let same = dependencies.every((item: any, index: any) => item === lastDependencies);
    if (same) {
        hookIndex++;
        return lastCallback;
        }
    }
    // 第一次渲染 或者 不是第一次但是依赖项相同,都返回新的
    hookStates[hookIndex++] = [callback, dependencies];
    return callback
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值