前提:加了hooks的代码在更新阶段增加了函数组件对hooks的处理
1.useState
流程
2.useEffect
流程
1.commitRoot是根节点的commit阶段的函数 commitWork是当前fiber的commit阶段(当前fiber一般是原生html元素的挂载阶段) 有hooks就不一样啦,函数组件也要commitWork
2.useEffectImpl做的事情:
将第一个参数的tag加到当前函数的fiber对象的tag上
将第二个参数的加到当前hook对象的memoizedState上 如果不是第一次渲染且inputs没变 则把noEffect这个tag加入到当前hook对象的memoizedState,同时把这个tag的effect对象放到函数fiber的componentUpdateQueu的effect这个链表上,finishhooks时候会把当前fiber的componentUpdateQueue放到updateQueue的effect链上,后续只要读取这个updateQueue的effect链就能拿到这个effect对象,然后去根据tag执行操作,这个tag是useEffectimpl传进来的第二个参数
接下来就是等commitHookEffectList执行
commitHookEffectList函数
这个函数作用就是看传进来的两个参数tag对比fiber.updateQueue链的effect的tag,有相交就执行destroy或者create函数
commitHookEffectList调用的地方
1.commitBeforeMutationLifeCycles:不会调用任何逻辑
2.commitWork:当有节点有update effect的时候会执行commitWork,对应dom会去挂载更新,对应函数组件会执行commitHookEffectList,这个部分有useLayouteffect会执行destoy方法
3.commitAlllifeCycles:
执行useLayout的create()函数,这个时候和类组件执行生命周期(didmount,didupdate)是同一个阶段
useEffect什么时候调用呢?
在commitRoot结束后,对rootFiber的effect链上的fiber如果该fiber有passive这个tag(useEffect会给fiber加上),那么进入异步调度执行一个函数,因为是异步调度且commitRoot对dom操作啦 所以肯定等本轮dom执行完才会执行这个函数,这个函数是遍历fiber的updateQueue上的effect链,先执行destroy,在执行create
useEffect和useLayouteffect的区别
useLayouteffect在commitRoot的commitLifeCycle阶段就会被执行,useEffect要异步等到我们浏览器渲染完成才会执行
useLayouteffect会阻碍浏览器渲染,我们在这个函数里面对dom进行更新或者setState是会同步到本次浏览器更新时,所以你的js很长就会阻塞
useEffect 会有闪动问题
3.useContext
直接读取传进来context,把他加到函数组件fiber的contextDepencyItem的链上,再返回context.currentValue即可
4.useRef
ref就是这样一个对象{current:value},会放到meroizedState上面
5.useImperativeHandle
当我们使用forwardRef的时候我们ref能够拿到完整的内部dom
function FancyInput(props, ref) {
return <input ref={ref} ... />;
}
FancyInput = forwardRef(FancyInput);
<FancyInput ref={inputRef} />
inputRef就是完整的dom
但是我们不让外部拿到那么多属性 因为操作完整的dom很危险 那么我们就可以修改暴露给外部ref的属性值
function FancyInput(props, ref) {
const innerRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
innerRef.current.focus();
}
}));
return <input ref={innerRef} ... />;
}
FancyInput = forwardRef(FancyInput);
let outref = useRef()
<FancyInput ref={outref} />
useImperativeHandle的源码就是把ref.current=匿名函数的返回值 即ref.current={focus:function} 那么我们外部拿到的ref就不是dom,而是{focus:function} 对象 只能执行focus方法
6.useCallback
每次传进来的都是新的函数,对比Inputs,一样的话返回merizedStated的callback(即之前的callback),不一样的话,则在该fiber上记录这个新的callback,返回新的callback
7.useMemo
和usecallback一样 唯一区别就是会执行callback返回的值存到fiber的merizedStated里面,useCallback是直接存函数