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个对象:
HooksDispatcherOnMountInDEV
HooksDispatcherOnMountWithHookTypesInDEV
HooksDispatcherOnUpdateInDEV
HooksDispatcherOnRerenderInDEV
InvalidNestedHooksDispatcherOnMountInDEV
InvalidNestedHooksDispatcherOnUpdateInDEV
InvalidNestedHooksDispatcherOnRerenderInDEV
这7个对象里都实现了一整套的useMemo、useCallback等,一整套的hooks。
但是结构都很相似,我们用useState来对比每个的不同。
先看HooksDispatcherOnMountInDEV
useState: 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)】再来看HooksDispatcherOnMountWithHookTypesInDEV
和HooksDispatcherOnMountInDEV只有一句的区别
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; } },

再看HooksDispatcherOnUpdateInDEV
和HooksDispatcherOnMountWithHookTypesInDEV只有一句的区别
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; } },

再看HooksDispatcherOnRerenderInDEV
这里需要说明,HooksDispatcherOnRerenderInDEV在renderWithHooks方法中是在后面赋值的,可以理解为是render是执行的。
和HooksDispatcherOnUpdateInDEV比,又只有一句的区别
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
这三个一块说。
InvalidNestedHooksDispatcherOnMountInDEV
useState: function (initialState) { currentHookNameInDev = 'useState'; // 都增加了这行 warnInvalidHookAccess(); mountHookTypesDev(); var prevDispatcher = ReactCurrentDispatcher$1.current; // 这行略有不同 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; try { return mountState(initialState); } finally { ReactCurrentDispatcher$1.current = prevDispatcher; } },

InvalidNestedHooksDispatcherOnUpdateInDEV:
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。

    推荐阅读