react|react hooks核心(hooktype和ReactCurrentDispatcher)
这里涉及整个hooks的一个入口函数:renderWithHooks
这个函数中只截取跟hookstype有关的部分:
{
if (current !== null && current.memoizedState !== null) {
// 如果是更新中,那么用HooksDispatcherOnUpdateInDEV
ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
} else if (hookTypesDev !== null) {
// This dispatcher handles an edge case where a component is updating,
// but no stateful hooks have been used.
// We want to match the production code behavior (which will use HooksDispatcherOnMount),
// but with the extra DEV validation to ensure hooks ordering hasn't changed.
// This dispatcher does that.// 如果是hookTypesDev不为空,那么用HooksDispatcherOnMountWithHookTypesInDEV
ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV;
} else {
// 默认情况:用HooksDispatcherOnMountInDEV,这也是一般的初始化的时候用的。
ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
}
}
接下来有7个对象:
HooksDispatcher
OnMount
InDEVHooksDispatcher
OnMountWithHookTypes
InDEVHooksDispatcher
OnUpdate
InDEVHooksDispatcher
OnRerender
InDEVInvalidNested
HooksDispatcherOnMount
InDEVInvalidNested
HooksDispatcherOnUpdate
InDEVInvalidNested
HooksDispatcherOnRerender
InDEV这7个对象里都实现了一整套的useMemo、useCallback等,一整套的hooks。
但是结构都很相似,我们用useState来对比每个的不同。
先看HooksDispatcher
OnMount
InDEVuseState: function (initialState) {
// 改变当前的hookname为useState
currentHookNameInDev = 'useState';
// 把hookname放进一个数组hookTypesDev里
mountHookTypesDev();
// 下面两行是迭代dispatch的。等于是把当前的存到prev里,然后让当前变成InvalidNestedHooksDispatcherOnMountInDEV
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return mountState(initialState);
} finally {
// 必定执行,将prev放到当前,因为不能再使用InvalidNestedHooksDispatcherOnMountInDEV了。
// 后面会说为什么不能再使用InvalidNestedHooksDispatcherOnMountInDEV
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
附:
mountHookTypesDev方法,很简单,就是把当前的hookname放进一个数组hookTypesDev里。
function mountHookTypesDev() {
{
var hookName = currentHookNameInDev;
if (hookTypesDev === null) {
hookTypesDev = [hookName];
} else {
hookTypesDev.push(hookName);
}
}
}
【react|react hooks核心(hooktype和ReactCurrentDispatcher)】再来看HooksDispatcher
OnMountWithHookTypes
InDEV和HooksDispatcher
OnMount
InDEV只有一句的区别useState: function (initialState) {
currentHookNameInDev = 'useState';
// 和HooksDispatcherOnMountInDEV只有这一行不同,这里的更新。其他完全一样。
updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
// 这里和HooksDispatcherOnMountInDEV一样。
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return mountState(initialState);
} finally {
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
再看HooksDispatcher
OnUpdate
InDEV和HooksDispatcher
OnMountWithHookTypes
InDEV只有一句的区别useState: function (initialState) {
currentHookNameInDev = 'useState';
updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
// 这里也是有区别的。但不太重要
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
// 和HooksDispatcherOnMountWithHookTypesInDEV只有这一句的区别
return updateState(initialState);
} finally {
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
再看HooksDispatcher
OnRerender
InDEV这里需要说明,HooksDispatcher
OnRerender
InDEV在renderWithHooks方法中是在后面赋值的,可以理解为是render是执行的。和HooksDispatcher
OnUpdate
InDEV比,又只有一句的区别useState: function (initialState) {
currentHookNameInDev = 'useState';
updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
// 这里也是有区别的。但不太重要
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
try {
// 只有这一句的区别
return rerenderState(initialState);
} finally {
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
InvalidNested
的mount、update、render这三个一块说。
InvalidNested
HooksDispatcherOnMount
InDEVuseState: function (initialState) {
currentHookNameInDev = 'useState';
// 都增加了这行
warnInvalidHookAccess();
mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
// 这行略有不同
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
return mountState(initialState);
} finally {
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
InvalidNested
HooksDispatcherOnUpdate
InDEV:useState: function (initialState) {
currentHookNameInDev = 'useState';
// 都增加了这行
warnInvalidHookAccess();
updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
// 这行略有不同,注意这里是update,不是render
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return rerenderState(initialState);
} finally {
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
这三部分,都是为了warnInvalidHookAccess这个警告代码:
不要在useEffect、useMemo里创建hooks,你只能在顶层的react组件中创建hooks。
源码如下:
var warnInvalidHookAccess = function () {
error('Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see ' + 'https://reactjs.org/link/rules-of-hooks');
};
总结
可以理解,每一个都是,如果在mountState、updateState、renderState区间,又做了mount、update、render的话,就会报警告,同时下一次还是在InvalidNested。
这个功能在state里,一个比较典型的场景是当setState方法里又执行了hooks方法,就会报error。
或者在useMemo、useEffect方法里创建hooks,也会报error。
推荐阅读
- react|react hooks源码核心(ReactCurrentDispatcher)
- react|react hooks 本质探索 - useMemo、useEffect源码解析
- 《液晶显示器和液晶电视维修核心教程》——第2章|《液晶显示器和液晶电视维修核心教程》——第2章 电子元器件的基础知识2.1 电阻类...
- react的”Hello|react的”Hello World !“
- React内部的性能优化没有达到极致()
- react函数组件使用React.memo避免重复渲染
- react-pdf|react-pdf 打造在线简历生成器的示例代码
- 【Copy攻城狮日志】React|【Copy攻城狮日志】React Native 集成 HMS Core
- Dubbo是什么?核心总结
- react中的双向绑定你真的了解吗