这里波及整个 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 个对象:
HooksDispatcherOnMount
InDEV
HooksDispatcherOnMountWithHookTypes
InDEV
HooksDispatcherOnUpdate
InDEV
HooksDispatcherOnRerender
InDEVInvalidNested
HooksDispatcherOnMount
InDEVInvalidNested
HooksDispatcherOnUpdate
InDEVInvalidNested
HooksDispatcherOnRerender
InDEV
这 7 个对象里都实现了一整套的 useMemo、useCallback 等,一整套的 hooks。
然而构造都很类似,咱们用 useState 来比照每个的不同。
先看 HooksDispatcherOnMount
InDEV
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);
}
}
}
再来看 HooksDispatcherOnMountWithHookTypes
InDEV
和 HooksDispatcherOnMount
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;}
},
再看 HooksDispatcherOnUpdate
InDEV
和 HooksDispatcherOnMountWithHookTypes
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;}
},
再看 HooksDispatcherOnRerender
InDEV
这里须要阐明,HooksDispatcherOnRerender
InDEV 在 renderWithHooks 办法中是在前面赋值的,能够了解为是 render 是执行的。
和 HooksDispatcherOnUpdate
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
InDEV
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;}
},
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。