常见React-hooks原理

useMemo

useMemo的功能是记忆某个结果,只有当依赖项发生改变时才会更新输出结果

组件初始化:

  • 执行计算函数,获取计算结果
  • 缓存计算结果和依赖项
  • 计算返回结果

组件更新:

  • 判断依赖项和缓存的依赖项是否相同,相同则直接返回已缓存的计算结果
  • 不同则执行计算函数,获取新的计算结果,缓存新计算结果和新的依赖项并返回计算结果

注意:

  • React会使用Object.is()对新旧依赖数组中的每一个元素进行强比较
  • 利用数组保存计算结果和依赖项,解决多次调用useMemo共享memorizedState的问题

实现:

  • let memorizedState=[];//利用数组解决多次调用useMemo共享memorizedState的问题
    let index=0;
    function useMemo(fn,deps){
        if(!memorizedState[index]){
            //组件初始化:缓存计算结果和依赖项,返回计算结果
            memorizedState[index++]=[fn(),deps];
            return fn();
        }else{ 
            //组件更新
            //判断依赖项是否改变
            const [lastValue,lastDeps]=memorizedState[index];
            const isSame=deps.every((item,idx)=>Object.is(item,lastDeps[idx]));
            if(isSame){
                //依赖项未改变,返回缓存的结果
                index++;
                return lastValue;
            }else{
                //依赖项改变,重新计算结果,缓存结果和依赖项,返回值
                memorizedState[index++]=[fn(),deps];
                return fn();
            }
        }
    }
    
useCallback

useCallback返回一个回调函数,该回调函数只会在依赖项改变时才会更新,回调函数更新后引用不相等,当将回调函数通过props传递给子组件时,会触发子组件重新渲染

实现原理和useMemo类似,不同的是useMemo缓存值,useCallback缓存函数

let memorizedState=[];//利用数组解决多次调用useCallback共享memorizedState的问题
let index=0;
function useCallback(fn,deps){
    if(!memorizedState[index]){
        //组件初始化:缓存函数和依赖项,返回函数
        memorizedState[index++]=[fn,deps];
        return fn;
    }else{
        //组件更新
        //判断依赖项是否改变 - Object.is()强比较
        const [lastFn,lastDeps]=memorizedState[index];
        const isSame=deps.every((item,idx)=>Object.is(item,lastDeps[idx]));
        if(isSame){
            //依赖项未改变,返回缓存的函数
            index++;
            return lastFn;
        }else{
            //依赖项改变,缓存函数和依赖项,返回函数
            memorizedState[index++]=[fn,deps];
            return fn;
        }
    }
}
useState

useState的功能是设置一个状态的初始值,并返回当前状态和设置状态的函数

实现思路:

  • 若初始状态为函数,则将函数执行结果作为当前状态,否则设置当前状态为初始状态
  • 生成设置状态函数
    • 若参数是函数,把prevState传入函数,将函数执行结果作为新的状态
    • 缓存最新状态
    • 触发视图更新render()
  • 返回当前状态和设置状态函数

注意:

  • 利用数组保存状态处理多次调用useState的情况
  • 利用闭包维护每个状态对应的设置状态函数

实现:

  • let memorizedState=[];
    let index=0;
    function useState(initialState){
        if(!memorizedState[index]){
            //若initialState是函数,则将函数执行结果作为当前状态
            memorizedState[index]=typeof initialState==='function'?initialState():initialState;
        }
        let curidx=index;//利用闭包维护每个状态对应的设置状态函数
        function setState(newState){
            if(typeof newState==='function'){
                //传入prevState
                newState=newState(memorizedState[curidx]);
            }
            //更新状态
            memorizedState[curidx]=newState;
            //触发视图更新
            render();
        }
        return [memorizedState[index++],setState];
    }
    
useReducer

useReducer和redux中的reducer很像,useState内部就是靠useReducer来实现的

useReducer初始化有两种形式:

  • 简单初始化:[state, dispatch] = useReducer(reducer, initialState),state初始值为initialState
  • 惰性初始化:[state, dispatch] = useReducer(reducer, initialArg, init),state初始值为init(initialArg)

实现思路:

  • state初始化
  • 生成dispatch
    • 调用reducer(state,action),将返回值作为更新后的状态
    • 触发视图更新render()
    • 注意参数action不能是函数
  • 返回当前状态和设置状态函数

实现:

  • let memorizedState=[];
    let index=0;
    function useReducer(reducer,initialState,init){
        if(!memorizedState[index]){
            //当传入了第三个参数init时,调用init(initialState),将返回结果作为状态初始值
            //否则初始值为initialState
            memorizedState[index]=init?init(initialState):initialState;
        }
        let curidx=index;
        function dispatch(action){
            if(typeof action==='function'){
                throw new Error(`${action.name} isn't allowed to be a function`);
            }
            //更新状态
            memorizedState[curidx]=reducer(memorizedState[curidx],action);
            //更新视图
            render();
        }
        return [memorizedState[index++],dispatch];
    }
    
useRef

useRef返回一个可变的ref对象,其current属性被初始化为传入的参数,这个ref对象在组件的整个生命周期内保持不变,也就是说每次重新渲染函数组件时,返回的ref对象都是同一个

注意:使用React.createRef时,每次重新渲染组件都会重新创建ref

let lastRef;
function useRef(value) {
  lastRef = lastRef || { current: value };
  return lastRef;
}
const container = useRef(null)
return <div ref={container}></div>

React 都会将 container.current 设置为对应的 DOM 节点

useEffect

useEffect给函数组件增加了操作副作用的能力,它跟类式组件中的componentDidMount、componentDidUpdate和componentWillUnMount具有相同的用途,只不过被合并成了一个API

不同的是,使用useEffect调度的effect不会阻塞浏览器更新视图,应用响应更快

useEffect的第二个参数:

  • 不传,组件每次render之后useEffect都会调用
  • 传入一个空数组,useEffect值会调用一次
  • 传入一个非空数组,依赖项发生变化时,触发useEffect执行

实现:

  • let memorizedState=[];
    let index=0;
    function useEffect(fn,deps){
        if(!memorizedState[index]){
            //组件初始化,缓存依赖,执行effect函数
            memorizedState[index++]=deps;
            fn();
        }else{
            //组件更新,判断依赖项是否改变
            const lastDeps=memorizedState[index];
            const isSame=deps.every((item,index)=>Object.is(item,lastDeps[index]));
            if(isSame){
                //依赖项未改变,不做任何操作
                index++;
            }else{
                //依赖项发生改变,缓存依赖,执行effect函数
                memorizedState[index++]=deps;
                fn();
            }
        }
    }
    
Object.is()

Object在严格相等的基础上修复了一些特殊情况下的失误(-0和+0,NaN和NaN)

Object.is=function(a,b){
    if(a===b){
        //运行到1/a===1/b时,a和b均为0,计算结果为+Infinity或-Infinity
        return a!==0||b!==0||1/a===1/b;
    }else{
        //NaN===NaN为false,会进入else
        //当a!==a为true时,a一定为NaN,b同理,当a、b同时为NaN时返回true
        return a!==a&&b!==b;
    }
}

console.log(Object.is(+0,-0));//false
console.log(Object.is(NaN,NaN));//true
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值