这一篇幅将具体介绍react自带的所有的react hooks源码的实现,文章的讲解主要集中在路径packages/react-reconciler/ReactFiberHooks.new.js,此文章需要你花费起码半个小时带上思考专心得看完,一定会有技术上的收获。
首先我们需要了解Hook类型:
包含了存储状态,基本状态,基本队列(Update类型中reducer,通过action触发状态改变),队列(类型UpdateQueue类型中dispatch 14个api),next指针指向下一个Hook对象。
react hooks官方的api提供现有14个。它们分别是useCallback,useContext,useEffect,useImperativeHandle,useLayoutEffect,useMemo, useReducer,useRef,useState,useDebugValue,useTransition,useDeferredValue, useMutableSource,useOpaqueIdentifier。我将会对它们一一讲解:
1. useCallback(使用回调)
useCallback组件加载时将回调加载上,在更新视图/重新渲染时更新回调。两者的公共代码部分都为将钩子的存储状态更新,根据return出来的回调更新视图。不同点在于在更新callback时会取之前钩子存储状态和现有分片调用时的参数进行比对。如相同则现有的callback就无意义,返回之前的状态
areHookInputsEqual会在其它的api处多次出现。正如函数字面意思先前状态和现有状态循环比对,相同则为true
2.useContext(使用内容)
无论是mount还是update都是调用readContext方法。从最后return的结果来看,它都是返回当前ReactContext的值。所以在调用这个方法时只能查看上下文容器, 如果有小伙伴对于ReactContext不懂的可以查看React Context(上下文) 作用和使用 看完不懂 你打我
3. useEffect(使用副作用)使用频率非常高
由代码可以看出副作用作用的为hook中的memoizedState,pushEffect的代码return出的effect所作用的为next属性也作用在componentUpdateQueue执行队列的最后执行副作用(lastEffect)中,从而实现状态的变更。
4. useImperativeHandle(使用紧急处理)
从return的mountEffectImpl和updateEffectImpl和useEffect类似,但区别在于imperativeHandleEffect.bind(null,create,ref)可以看出其是要绑定ref指向,而函数组件上不能使用ref属性,所以useImperativeHandle要结合ForwardRef一起使用。实现effect的变更。 使用可以参考useImperativeHandle的使用
5. useLayoutEffect(使用布局效果)
从return的mountEffectImpl和updateEffectImpl和useEffect类似。唯一的区别在于第二个参数hookLayout,所以可以把useLayoutEffect理解为useEffect+dom处理。
当useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题。
6. useMemo(使用备忘录)
源代码和useCallback基本类似,唯一的区别return出来的是nextValue的值,正如字面意思备忘录,备忘录会实时更新,里面的状态可以随时取出来翻看查阅修改,所以非常适合取代(HOC)高阶组件,典型案例就是react-router-dom中的useHistory,antd 4.x版本form组件中的useForm,其内核都是用useMemo实现
7.useReducer(使用reducer)
updateReducer和rerenderReducer两个方法代码太长不贴上来,小伙伴可在源代码中自行查看。在使用上和redux下的reducer完全相同。源代码实现上通过dispatch内置的action来改变hook的memoizedState。
8.useRef(使用ref)
在加载时return当前的ref实例,在更新时返回在加载时保存的存储状态。useRef的使用参考React—useRef 。
9.useState(使用频率非常高)
从源代码可以看出和useReducer有千丝万缕的联系,或者可以理解为useState是useReducer的mini缩小版本,将useReducer精准拆分。所以在实现上有更大的灵活性
10.useDebugValue
这个hook现在基本为空,无需理会
11.useTransition(使用过渡)
过渡即为处理页面上从一个状态到另一个状态或者从一个页面到另一个页面,实现平缓的处理,源码实现上结合来useRef+useState解决pending状态值变更。
在加载时将当前dom实例保存并存储状态,在开启过渡startTransition函数中,拉取当前优先执行队列级,通过不断比对优先级runWithPriority()函数,只有在队列优先等级大于UserBlockingPriority(98)则必然会大于NormalPriority(97),runWithPriority接收普通执行队列时才会判定不用再继续维持pending状态,返回页面状态。
更直白的话是对于高优先级的更新/当前事务繁忙时,调度到下一空闲期再应用,但也可能马上就被应用。具体使用可以参考useTransition 的平行世界
12.useDeferredValue(使用延期值)
源代码明显可以和useTransition联系,使用useEffect监听value的变化,然后在useTransition中的startTransition方法中更新value。从而实现延迟更新的效果。
13. useMutableSource(使用易变资源)
源代码上入参为资源,快照。在实现上是采用了递归的方式对可变资源source,上一次的状态截图并实时subscribe订阅。
facebook官方设计的意图为在并发模式下能使react组件安全且有效得读取外部传入的时刻改动的资源并实时监控。
具体使用示例
const ReactReduxContext = React.createContext();
function Provider({ store, children }) {
const config = useMemo(
() => ({
getSnapshot: store => store.getState(),
subscribe: (store, callback) => store.subscribe(callback)
}),
[reduxSource] // how to go from store to mutable source?
);
const state = useMutableSource(reduxSource, config);
const contextValue = useMemo(() = ({ state, store }), [state, store]);
return (
<ReactReduxContext.Provider value={contextValue}>
{children}
</ReactReduxContext.Provider>
);
}
14.useOpaqueIdentifier(使用不透明标识)
在加载时return出来的id,更新和重新渲染时通过useState更新的id。此id即为标识。
hooks的14个api源代码已解释完毕,希望对你的react技术学习带来成长。