带你从0~1手写React源码--实现react中的hooks

223 阅读2分钟

useState

话不多说,上代码! 没错就是这么简单

//hook的索引,表示当前的index
let hookIndex=0;
//这里是一个数组,里面存放着我们所有的状态
let hookStates=[];

function render(vdom,container){
    mount(vdom,container)
    scheduleUpdate=()=>{
        hookIndex=0;//在状态修改后调试更新的时候,索引重置为0
        compareTwoVdom(container,vdom,vdom)
    }
} 
export function useState(initialState){
    hookStates[hookIndex]=hookStates[hookIndex]||(typeof initialState==='function'?initialState():initialState);
    let currentIndex=hookStates[hookIndex];
    function setState(newState){
        newState=typeof newState==='function'?newState(hookStates[currentIndex]):newState;
        hookStates[currentIndex]=newState;
        scheduleUpdate();//状态改变后重新更新应用
    }
    return [hookStates[hookIndex++],setState]
}

useCallback

话不多说,上代码! 没错就是这么简单

export function useCallback(callback,deps){
    if(hookStates[hookIndex]){
        const [lastCallback,lastDeps]=hookStates[hookIndex];
        let same=deps.every((item,index)=>item===lastDeps[index]);
        if(same){
            hookIndex++;
            return lastCallback;
        }else{  
            hookStates[hookIndex++]=[callback,deps];
            return callback
        }
    }else{
        hookStates[hookIndex++]=[callback,deps]
        return callback
    }
}

useMemo

话不多说,上代码! 没错就是这么简单

export function useMemo(factory,deps){
    if(hookStates[hookIndex]){
        let [lastMemo,lastDeps]=hookStates[hookIndex];
        let same=deps.every((item,index)=>item===lastDeps[index]);
        if(same){
            hookIndex++;
            return lastMemo
        }else{
            let newMemo=factory();
            hookStates[hookIndex++]=[newMemo,deps];
            return newMemo
        }
    }else{
        let newMemo=factory();
        hookStates[hookIndex++]=[newMemo,deps]
        return newMemo
    }
}

memo

话不多说,上代码! 没错就是这么简单

function Memo(functionComponent){
    return class extends PureComponent{
        render(){
            return functionComponent(this.props)
        }
    }
}

useReducer

话不多说,上代码! 没错就是这么简单

function useReducer(reducer,initialState){
    hookStates[hookIndex]=hookStates[hookIndex]||initialState;
    let currentIndex=hookIndex;
    function dispatch(action){
        hookStates[currentIndex]=reducer(hookStates[currentIndex],action);
        scheduleUpdate();
    }
    return [hookStates[hookIndex++,dispatch]]
}

useContext

function createContext(initialValue) {
    const context={Provider,Consumer}
    function Provider(props) { 
        context._currentValue=context._currentValue||initialValue;
        Object.assign(context._currentValue,props.value)
        // Provider._value=props.value;
        return props.children
    }
    function Consumer(props){
        props.children(Provider._value)
    }
    return {Provider,Consumer}
}

function useContext(context) {
  return context._currentValue
}

useEffect

function useEffect(callback,deps){
    if(hookStates[hookIndex]){
        let [functionDestory,lastDeps]=hookStates[hookIndex];
        let same=deps.every((item,index)=>item===lastDeps[index]);
        if(same){
            hookIndex++;
        }else{
            functionDestory&&functionDestory();
            //把回调放在了宏任务队列中,保证此回调函数不是同步执行,而是在页面渲染后执行
            setTimeout(()=>{
                let functionDestory = callback();
                hookStates[hookIndex++] = [functionDestory,deps];
            });
        }
    }else{
        setTimeout(()=>{
            let functionDestory=callback();
            hookStates[hookIndex++]=[functionDestory,deps];
        })
    }
}

useLayoutEffect

function useLayoutEffect(callback,deps){
    if(hookStates[hookIndex]){
        let [functionDestory,lastDeps]=hookStates[hookIndex];
        let same=deps.every((item,index)=>item===lastDeps[index]);
        if(same){
            hookIndex++;
        }else{
            functionDestory&&functionDestory();
            //把回调放在了宏任务队列中
            queueMicrotask(()=>{
                let functionDestory = callback();
                hookStates[hookIndex++] = [functionDestory,deps];
            });
        }
    }else{
        queueMicrotask(()=>{
            let functionDestory=callback();
            hookStates[hookIndex++]=[functionDestory,deps];
        })
    }
}

forwaredRefuseImperativeHandle

function forwardRef(FunctionComponent){
    return class extends Component{
        render(){
            return FunctionComponent(this.props,this.ref)
        }
    }
}
function useImperativeHandle(ref,factory){
    ref.current=factory();
}

useRequest向后台发送分页请求

function useRequest(url){
    let limit=5;
    let [offset,setOffset]=useState(0);
    let [data,setData]=useState([]);
    function loadMore(){
        setData(null); //这里的null是为了在异步请求数据还没有回来的时候设置data=NULL去显示加载中的状态
        fetch(`${url}?offset=${offset}&limit=${limit}`).then(res=>res.json()).then(newData=>{
            setData([...data,...newData]);//data是上次的data,不是null,闭包机制
            setOffset(offset+newData.length);
        })
    }
    useEffect(()=>{
        loadMore()
    },[])
    return [data,loadMore]
}