@@ -1,4 +1,4 @@
-/** @license React v16.8.3
+/** @license React v16.8.4
* react-dom.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
@@ -10001,6 +10001,7 @@
this._debugSource = null;
this._debugOwner = null;
this._debugIsCurrentlyTiming = false;
+ this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this);
}
@@ -10068,6 +10069,7 @@
workInProgress._debugID = current._debugID;
workInProgress._debugSource = current._debugSource;
workInProgress._debugOwner = current._debugOwner;
+ workInProgress._debugHookTypes = current._debugHookTypes;
}
workInProgress.alternate = current;
@@ -10335,6 +10337,7 @@
target._debugSource = source._debugSource;
target._debugOwner = source._debugOwner;
target._debugIsCurrentlyTiming = source._debugIsCurrentlyTiming;
+ target._debugHookTypes = source._debugHookTypes;
return target;
}
@@ -12720,7 +12723,6 @@
// current hook list is the list that belongs to the current fiber. The
// work-in-progress hook list is a new list that will be added to the
// work-in-progress fiber.
-var firstCurrentHook = null;
var currentHook = null;
var nextCurrentHook = null;
var firstWorkInProgressHook = null;
@@ -12750,23 +12752,53 @@
// In DEV, this is the name of the currently executing primitive hook
var currentHookNameInDev = null;
-function warnOnHookMismatchInDev() {
+// In DEV, this list ensures that hooks are called in the same order between renders.
+// The list stores the order of hooks used during the initial render (mount).
+// Subsequent renders (updates) reference this list.
+var hookTypesDev = null;
+var hookTypesUpdateIndexDev = -1;
+
+function mountHookTypesDev() {
+ {
+ var hookName = currentHookNameInDev;
+
+ if (hookTypesDev === null) {
+ hookTypesDev = [hookName];
+ } else {
+ hookTypesDev.push(hookName);
+ }
+ }
+}
+
+function updateHookTypesDev() {
+ {
+ var hookName = currentHookNameInDev;
+
+ if (hookTypesDev !== null) {
+ hookTypesUpdateIndexDev++;
+ if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
+ warnOnHookMismatchInDev(hookName);
+ }
+ }
+ }
+}
+
+function warnOnHookMismatchInDev(currentHookName) {
{
var componentName = getComponentName(currentlyRenderingFiber$1.type);
if (!didWarnAboutMismatchedHooksForComponent.has(componentName)) {
didWarnAboutMismatchedHooksForComponent.add(componentName);
- var secondColumnStart = 22;
-
+ if (hookTypesDev !== null) {
var table = '';
- var prevHook = firstCurrentHook;
- var nextHook = firstWorkInProgressHook;
- var n = 1;
- while (prevHook !== null && nextHook !== null) {
- var oldHookName = prevHook._debugType;
- var newHookName = nextHook._debugType;
- var row = n + '. ' + oldHookName;
+ var secondColumnStart = 30;
+
+ for (var i = 0; i <= hookTypesUpdateIndexDev; i++) {
+ var oldHookName = hookTypesDev[i];
+ var newHookName = i === hookTypesUpdateIndexDev ? currentHookName : oldHookName;
+
+ var row = i + 1 + '. ' + oldHookName;
// Extra space so second column lines up
// lol @ IE not supporting String#repeat
@@ -12777,12 +12809,10 @@
row += newHookName + '\n';
table += row;
- prevHook = prevHook.next;
- nextHook = nextHook.next;
- n++;
}
- warning$1(false, 'React has detected a change in the order of Hooks called by %s. ' + 'This will lead to bugs and errors if not fixed. ' + 'For more information, read the Rules of Hooks: https://fb.me/rules-of-hooks\n\n' + ' Previous render Next render\n' + ' -------------------------------\n' + '%s' + ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n', componentName, table);
+ warning$1(false, 'React has detected a change in the order of Hooks called by %s. ' + 'This will lead to bugs and errors if not fixed. ' + 'For more information, read the Rules of Hooks: https://fb.me/rules-of-hooks\n\n' + ' Previous render Next render\n' + ' ------------------------------------------------------\n' + '%s' + ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n', componentName, table);
+ }
}
}
}
@@ -12818,7 +12848,12 @@
function renderWithHooks(current, workInProgress, Component, props, refOrContext, nextRenderExpirationTime) {
renderExpirationTime = nextRenderExpirationTime;
currentlyRenderingFiber$1 = workInProgress;
- firstCurrentHook = nextCurrentHook = current !== null ? current.memoizedState : null;
+ nextCurrentHook = current !== null ? current.memoizedState : null;
+
+ {
+ hookTypesDev = current !== null ? current._debugHookTypes : null;
+ hookTypesUpdateIndexDev = -1;
+ }
// The following should have already been reset
// currentHook = null;
@@ -12832,8 +12867,26 @@
// numberOfReRenders = 0;
// sideEffectTag = 0;
+ // TODO Warn if no hooks are used at all during mount, then some are used during update.
+ // Currently we will identify the update render as a mount because nextCurrentHook === null.
+ // This is tricky because it's valid for certain types of components (e.g. React.lazy)
+
+ // Using nextCurrentHook to differentiate between mount/update only works if at least one stateful hook is used.
+ // Non-stateful hooks (e.g. context) don't get added to memoizedState,
+ // so nextCurrentHook would be null during updates and mounts.
{
- ReactCurrentDispatcher$1.current = nextCurrentHook === null ? HooksDispatcherOnMountInDEV : HooksDispatcherOnUpdateInDEV;
+ if (nextCurrentHook !== null) {
+ 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.
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV;
+ } else {
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
+ }
}
var children = Component(props, refOrContext);
@@ -12844,13 +12897,18 @@
numberOfReRenders += 1;
// Start over from the beginning of the list
- firstCurrentHook = nextCurrentHook = current !== null ? current.memoizedState : null;
+ nextCurrentHook = current !== null ? current.memoizedState : null;
nextWorkInProgressHook = firstWorkInProgressHook;
currentHook = null;
workInProgressHook = null;
componentUpdateQueue = null;
+ {
+ // Also validate hook order for cascading updates.
+ hookTypesUpdateIndexDev = -1;
+ }
+
ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
children = Component(props, refOrContext);
@@ -12860,10 +12918,6 @@
numberOfReRenders = 0;
}
- {
- currentHookNameInDev = null;
- }
-
// We can assume the previous dispatcher is always this one, since we set it
// at the beginning of the render phase and there's no re-entrancy.
ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
@@ -12875,18 +12929,29 @@
renderedWork.updateQueue = componentUpdateQueue;
renderedWork.effectTag |= sideEffectTag;
+ {
+ renderedWork._debugHookTypes = hookTypesDev;
+ }
+
+ // This check uses currentHook so that it works the same in DEV and prod bundles.
+ // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
renderExpirationTime = NoWork;
currentlyRenderingFiber$1 = null;
- firstCurrentHook = null;
currentHook = null;
nextCurrentHook = null;
firstWorkInProgressHook = null;
workInProgressHook = null;
nextWorkInProgressHook = null;
+ {
+ currentHookNameInDev = null;
+ hookTypesDev = null;
+ hookTypesUpdateIndexDev = -1;
+ }
+
remainingExpirationTime = NoWork;
componentUpdateQueue = null;
sideEffectTag = 0;
@@ -12920,21 +12985,23 @@
renderExpirationTime = NoWork;
currentlyRenderingFiber$1 = null;
- firstCurrentHook = null;
currentHook = null;
nextCurrentHook = null;
firstWorkInProgressHook = null;
workInProgressHook = null;
nextWorkInProgressHook = null;
- remainingExpirationTime = NoWork;
- componentUpdateQueue = null;
- sideEffectTag = 0;
-
{
+ hookTypesDev = null;
+ hookTypesUpdateIndexDev = -1;
+
currentHookNameInDev = null;
}
+ remainingExpirationTime = NoWork;
+ componentUpdateQueue = null;
+ sideEffectTag = 0;
+
didScheduleRenderPhaseUpdate = false;
renderPhaseUpdates = null;
numberOfReRenders = 0;
@@ -12951,9 +13018,6 @@
next: null
};
- {
- hook._debugType = currentHookNameInDev;
- }
if (workInProgressHook === null) {
// This is the first hook in the list
firstWorkInProgressHook = workInProgressHook = hook;
@@ -13000,13 +13064,6 @@
workInProgressHook = workInProgressHook.next = newHook;
}
nextCurrentHook = currentHook.next;
-
- {
- newHook._debugType = currentHookNameInDev;
- if (currentHookNameInDev !== currentHook._debugType) {
- warnOnHookMismatchInDev();
- }
- }
}
return workInProgressHook;
}
@@ -13021,20 +13078,6 @@
return typeof action === 'function' ? action(state) : action;
}
-function mountContext(context, observedBits) {
- {
- mountWorkInProgressHook();
- }
- return readContext(context, observedBits);
-}
-
-function updateContext(context, observedBits) {
- {
- updateWorkInProgressHook();
- }
- return readContext(context, observedBits);
-}
-
function mountReducer(reducer, initialArg, init) {
var hook = mountWorkInProgressHook();
var initialState = void 0;
@@ -13527,6 +13570,7 @@
};
var HooksDispatcherOnMountInDEV = null;
+var HooksDispatcherOnMountWithHookTypesInDEV = null;
var HooksDispatcherOnUpdateInDEV = null;
var InvalidNestedHooksDispatcherOnMountInDEV = null;
var InvalidNestedHooksDispatcherOnUpdateInDEV = null;
@@ -13546,26 +13590,32 @@
},
useCallback: function (callback, deps) {
currentHookNameInDev = 'useCallback';
+ mountHookTypesDev();
return mountCallback(callback, deps);
},
useContext: function (context, observedBits) {
currentHookNameInDev = 'useContext';
- return mountContext(context, observedBits);
+ mountHookTypesDev();
+ return readContext(context, observedBits);
},
useEffect: function (create, deps) {
currentHookNameInDev = 'useEffect';
+ mountHookTypesDev();
return mountEffect(create, deps);
},
useImperativeHandle: function (ref, create, deps) {
currentHookNameInDev = 'useImperativeHandle';
+ mountHookTypesDev();
return mountImperativeHandle(ref, create, deps);
},
useLayoutEffect: function (create, deps) {
currentHookNameInDev = 'useLayoutEffect';
+ mountHookTypesDev();
return mountLayoutEffect(create, deps);
},
useMemo: function (create, deps) {
currentHookNameInDev = 'useMemo';
+ mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
@@ -13576,6 +13626,7 @@
},
useReducer: function (reducer, initialArg, init) {
currentHookNameInDev = 'useReducer';
+ mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
@@ -13586,10 +13637,12 @@
},
useRef: function (initialValue) {
currentHookNameInDev = 'useRef';
+ mountHookTypesDev();
return mountRef(initialValue);
},
useState: function (initialState) {
currentHookNameInDev = 'useState';
+ mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
@@ -13600,6 +13653,81 @@
},
useDebugValue: function (value, formatterFn) {
currentHookNameInDev = 'useDebugValue';
+ mountHookTypesDev();
+ return mountDebugValue(value, formatterFn);
+ }
+ };
+
+ HooksDispatcherOnMountWithHookTypesInDEV = {
+ readContext: function (context, observedBits) {
+ return readContext(context, observedBits);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = 'useCallback';
+ updateHookTypesDev();
+ return mountCallback(callback, deps);
+ },
+ useContext: function (context, observedBits) {
+ currentHookNameInDev = 'useContext';
+ updateHookTypesDev();
+ return readContext(context, observedBits);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = 'useEffect';
+ updateHookTypesDev();
+ return mountEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = 'useImperativeHandle';
+ updateHookTypesDev();
+ return mountImperativeHandle(ref, create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = 'useLayoutEffect';
+ updateHookTypesDev();
+ return mountLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = 'useMemo';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = 'useReducer';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = 'useRef';
+ updateHookTypesDev();
+ return mountRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = 'useState';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = 'useDebugValue';
+ updateHookTypesDev();
return mountDebugValue(value, formatterFn);
}
};
@@ -13610,26 +13738,32 @@
},
useCallback: function (callback, deps) {
currentHookNameInDev = 'useCallback';
+ updateHookTypesDev();
return updateCallback(callback, deps);
},
useContext: function (context, observedBits) {
currentHookNameInDev = 'useContext';
- return updateContext(context, observedBits);
+ updateHookTypesDev();
+ return readContext(context, observedBits);
},
useEffect: function (create, deps) {
currentHookNameInDev = 'useEffect';
+ updateHookTypesDev();
return updateEffect(create, deps);
},
useImperativeHandle: function (ref, create, deps) {
currentHookNameInDev = 'useImperativeHandle';
+ updateHookTypesDev();
return updateImperativeHandle(ref, create, deps);
},
useLayoutEffect: function (create, deps) {
currentHookNameInDev = 'useLayoutEffect';
+ updateHookTypesDev();
return updateLayoutEffect(create, deps);
},
useMemo: function (create, deps) {
currentHookNameInDev = 'useMemo';
+ updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
@@ -13640,6 +13774,7 @@
},
useReducer: function (reducer, initialArg, init) {
currentHookNameInDev = 'useReducer';
+ updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
@@ -13650,10 +13785,12 @@
},
useRef: function (initialValue) {
currentHookNameInDev = 'useRef';
+ updateHookTypesDev();
return updateRef(initialValue);
},
useState: function (initialState) {
currentHookNameInDev = 'useState';
+ updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
@@ -13664,6 +13801,7 @@
},
useDebugValue: function (value, formatterFn) {
currentHookNameInDev = 'useDebugValue';
+ updateHookTypesDev();
return updateDebugValue(value, formatterFn);
}
};
@@ -13676,31 +13814,37 @@
useCallback: function (callback, deps) {
currentHookNameInDev = 'useCallback';
warnInvalidHookAccess();
+ mountHookTypesDev();
return mountCallback(callback, deps);
},
useContext: function (context, observedBits) {
currentHookNameInDev = 'useContext';
warnInvalidHookAccess();
- return mountContext(context, observedBits);
+ mountHookTypesDev();
+ return readContext(context, observedBits);
},
useEffect: function (create, deps) {
currentHookNameInDev = 'useEffect';
warnInvalidHookAccess();
+ mountHookTypesDev();
return mountEffect(create, deps);
},
useImperativeHandle: function (ref, create, deps) {
currentHookNameInDev = 'useImperativeHandle';
warnInvalidHookAccess();
+ mountHookTypesDev();
return mountImperativeHandle(ref, create, deps);
},
useLayoutEffect: function (create, deps) {
currentHookNameInDev = 'useLayoutEffect';
warnInvalidHookAccess();
+ mountHookTypesDev();
return mountLayoutEffect(create, deps);
},
useMemo: function (create, deps) {
currentHookNameInDev = 'useMemo';
warnInvalidHookAccess();
+ mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
@@ -13712,6 +13856,7 @@
useReducer: function (reducer, initialArg, init) {
currentHookNameInDev = 'useReducer';
warnInvalidHookAccess();
+ mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
@@ -13723,11 +13868,13 @@
useRef: function (initialValue) {
currentHookNameInDev = 'useRef';
warnInvalidHookAccess();
+ mountHookTypesDev();
return mountRef(initialValue);
},
useState: function (initialState) {
currentHookNameInDev = 'useState';
warnInvalidHookAccess();
+ mountHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
try {
@@ -13739,6 +13886,7 @@
useDebugValue: function (value, formatterFn) {
currentHookNameInDev = 'useDebugValue';
warnInvalidHookAccess();
+ mountHookTypesDev();
return mountDebugValue(value, formatterFn);
}
};
@@ -13751,31 +13899,37 @@
useCallback: function (callback, deps) {
currentHookNameInDev = 'useCallback';
warnInvalidHookAccess();
+ updateHookTypesDev();
return updateCallback(callback, deps);
},
useContext: function (context, observedBits) {
currentHookNameInDev = 'useContext';
warnInvalidHookAccess();
- return updateContext(context, observedBits);
+ updateHookTypesDev();
+ return readContext(context, observedBits);
},
useEffect: function (create, deps) {
currentHookNameInDev = 'useEffect';
warnInvalidHookAccess();
+ updateHookTypesDev();
return updateEffect(create, deps);
},
useImperativeHandle: function (ref, create, deps) {
currentHookNameInDev = 'useImperativeHandle';
warnInvalidHookAccess();
+ updateHookTypesDev();
return updateImperativeHandle(ref, create, deps);
},
useLayoutEffect: function (create, deps) {
currentHookNameInDev = 'useLayoutEffect';
warnInvalidHookAccess();
+ updateHookTypesDev();
return updateLayoutEffect(create, deps);
},
useMemo: function (create, deps) {
currentHookNameInDev = 'useMemo';
warnInvalidHookAccess();
+ updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
@@ -13787,6 +13941,7 @@
useReducer: function (reducer, initialArg, init) {
currentHookNameInDev = 'useReducer';
warnInvalidHookAccess();
+ updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
@@ -13798,11 +13953,13 @@
useRef: function (initialValue) {
currentHookNameInDev = 'useRef';
warnInvalidHookAccess();
+ updateHookTypesDev();
return updateRef(initialValue);
},
useState: function (initialState) {
currentHookNameInDev = 'useState';
warnInvalidHookAccess();
+ updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
@@ -13814,6 +13971,7 @@
useDebugValue: function (value, formatterFn) {
currentHookNameInDev = 'useDebugValue';
warnInvalidHookAccess();
+ updateHookTypesDev();
return updateDebugValue(value, formatterFn);
}
};
@@ -20548,7 +20706,7 @@
// TODO: this is special because it gets imported during build.
-var ReactVersion = '16.8.3';
+var ReactVersion = '16.8.4';
// TODO: This type is shared between the reconciler and ReactDOM, but will
// eventually be lifted out to the renderer.