react hook实现

render方法

function render(){
    
    
    ReactDOM.render(
        <Counter />,
        document.getElementById('root')
    );
}
render()

useState

  • 全局数组来保存这些state
  • 闭包索引来指向每一次对应的useState
let lastState = []  //存储每一次useState的值
let stateIndex = 0
function useState(initState) {
    
    
	//进行保存
    lastState[stateIndex] = lastState[stateIndex] || initState;
    //索引指向当前state存储的位置
    const currentIndex = stateIndex
    function setState(newState) {
    
    
    	//通过闭包保存每一个state的索引
        lastState[currentIndex ] = newState
        //重新渲染
        render()
    }
    return [lastState[stateIndex++],setState]
}

useCallback
当传递子组件是一个方法时候,看似方法没改变,其实旧的和新的方法是不一样的,会重新创建函数,每一次点击按钮子组件都会重新渲染。

useCallback则会使用全局变量保存函数地址,使得不会重新渲染组件

  • 用全局变量把callback和dependencies依赖项保存下来。
  • 首先useCallback会判断我们是否传入了依赖项,如果没有传的话,说明要每一次执行useCallback都返回最新的callback
//存储的callback
let lastCallback
//依赖项
let lastCallbackDependencies

function useCallback(callback,dependencies){
    
    
	//如果传入了依赖项
    if(lastCallbackDependencies){
    
    
    	//判断依赖项是否改变
        let changed = !dependencies.every((item,index)=>{
    
    
            return item === lastCallbackDependencies[index]
        })
        if(changed){
    
    
            lastCallback = callback
            lastCallbackDependencies = dependencies
        }
    }else{
    
     // 没有传入依赖项,则每次都要改变
        
        lastCallback = callback
        lastCallbackDependencies = dependencies
    }
    return lastCallback
}

useMemo

  • seMemo用于缓存值
let lastMemo
let lastMemoDependencies

function useMemo(callback,dependencies){
    
    
	//判断是否传入依赖项
    if(lastMemoDependencies){
    
    
        let changed = !dependencies.every((item,index)=>{
    
    
            return item === lastMemoDependencies[index]
        })
       	//依赖项改变重新计算赋值
        if(changed){
    
    
            lastMemo = callback()
            lastMemoDependencies = dependencies
        }
    }else{
    
     // 没有传入依赖项,每次都重新计算赋值
        lastMemo = callback()
        lastMemoDependencies = dependencies
    }
    return lastMemo
}

useReducer
对于复杂的state结构,推荐使用useReducer,useReducer可以看作是useState的复杂增强版

基本使用:

// 官方 useReducer Demo
// 第一个参数:应用的初始化
const initialState = {
    
    count: 0};

// 第二个参数:state的reducer处理函数
function reducer(state, action) {
    
    
    switch (action.type) {
    
    
        case 'increment':
          return {
    
    count: state.count + 1};
        case 'decrement':
           return {
    
    count: state.count - 1};
        default:
            throw new Error();
    }
}

function Counter() {
    
    
    // 返回值:最新的state和dispatch函数
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <>
            // useReducer会根据dispatch的action,返回最终的state,并触发rerender
            Count: {
    
    state.count}
            // dispatch 用来接收一个 action参数「reducer中的action」,用来触发reducer函数,更新最新的状态
            <button onClick={
    
    () => dispatch({
    
    type: 'increment'})}>+</button>
            <button onClick={
    
    () => dispatch({
    
    type: 'decrement'})}>-</button>
        </>
    );
}

实现原理:

//全局保存每一次dipatch后state的值
let lastState

function useReducer(reducer,initialState){
    
    
    lastState = lastState || initialState
    function dispatch(action){
    
    
    	//调用传入的reducer方法,和action的type类型即可
        lastState = reducer(lastState,action)
        //重新渲染
        render()
    }
    //返回最新值和type方法
    return [lastState,dispatch]
}

useEffect

  • 作为第一个参数的函数是在浏览器渲染结束后执行的即宏任务-》渲染-》宏任务
  • 类似生命周期效果的实现,实际每次都是setState后重新render过后,重新执行组件,重新执行useEffect
//全局保存依赖项
let lastEffectDependencies
function useEffect(callback,dependencies){
    
    
	//如果传入了依赖项
    if(lastEffectDependencies){
    
    
        let changed = !dependencies.every((item,index)=>{
    
    
            return item === lastEffectDependencies[index]
        })
        //如果依赖项改变,重新执行并赋值
        if(changed){
    
    
            setTimeout(callback())
            lastEffectDependencies = dependencies
        }
    }else{
    
     
    	//未传入依赖项,每次都执行
        setTimeout(callback())
        lastEffectDependencies = dependencies
    }
}

useLayoutEffect

  • 原理跟useEffect一样,只是调用时机不同
  • useEffect的调用时机是浏览器渲染结束后执行的,而useLayoutEffect是在DOM构建完成,浏览器渲染前执行的即宏任务(微任务,这么写原因是微任务是宏任务的一部分)->渲染->宏任务(微任务)
let lastEffectDependencies
function useLayouyEffect(callback,dependencies){
    
    
    if(lastEffectDependencies){
    
    
        let changed = !dependencies.every((item,index)=>{
    
    
            return item === lastEffectDependencies[index]
        })
        if(changed){
    
    
            Promise.resolve().then(callback())
            lastEffectDependencies = dependencies
        }
    }else{
    
     
        Promise.resolve().then(callback())
        lastEffectDependencies = dependencies
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43294560/article/details/121795879