Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8a73b5eb6 | ||
|
|
741aa17a33 | ||
|
|
95c2b49543 | ||
|
|
55cf14f98e | ||
|
|
29b7b775f2 | ||
|
|
b668168d4d | ||
|
|
619cdfc624 |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -6,6 +6,22 @@
|
||||
</summary>
|
||||
</details>
|
||||
|
||||
## 16.8.4 (March 5, 2019)
|
||||
|
||||
### React DOM and other renderers
|
||||
|
||||
- Fix a bug where DevTools caused a runtime error when inspecting a component that used a `useContext` hook. ([@bvaughn](https://github.com/bvaughn) in [#14940](https://github.com/facebook/react/pull/14940))
|
||||
|
||||
## 16.8.3 (February 21, 2019)
|
||||
|
||||
### React DOM
|
||||
|
||||
- Fix a bug that caused inputs to behave incorrectly in UMD builds. ([@gaearon](https://github.com/gaearon) in [#14914](https://github.com/facebook/react/pull/14914))
|
||||
- Fix a bug that caused render phase updates to be discarded. ([@gaearon](https://github.com/gaearon) in [#14852](https://github.com/facebook/react/pull/14852))
|
||||
|
||||
### React DOM Server
|
||||
- Unwind the context stack when a stream is destroyed without completing, to prevent incorrect values during a subsequent render. ([@overlookmotel](https://github.com/overlookmotel) in [#14706](https://github.com/facebook/react/pull/14706/))
|
||||
|
||||
## 16.8.2 (February 14, 2019)
|
||||
|
||||
### React DOM
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "create-subscription",
|
||||
"description": "utility for subscribing to external data sources inside React components",
|
||||
"version": "16.8.2",
|
||||
"version": "16.8.4",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "eslint-plugin-react-hooks",
|
||||
"description": "ESLint rules for React Hooks",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jest-react",
|
||||
"version": "0.6.2",
|
||||
"version": "0.6.4",
|
||||
"description": "Jest matchers and utilities for testing React components.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-art",
|
||||
"description": "React ART is a JavaScript library for drawing vector graphics using React. It provides declarative and reactive bindings to the ART library. Using the same declarative API you can render the output to either Canvas, SVG or VML (IE8).",
|
||||
"version": "16.8.2",
|
||||
"version": "16.8.4",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -27,7 +27,7 @@
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.13.2"
|
||||
"scheduler": "^0.13.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0"
|
||||
|
||||
@@ -427,4 +427,42 @@ describe('ReactHooksInspectionIntegration', () => {
|
||||
expect(setterCalls[0]).not.toBe(initial);
|
||||
expect(setterCalls[1]).toBe(initial);
|
||||
});
|
||||
|
||||
// This test case is based on an open source bug report:
|
||||
// facebookincubator/redux-react-hook/issues/34#issuecomment-466693787
|
||||
it('should properly advance the current hook for useContext', () => {
|
||||
const MyContext = React.createContext(1);
|
||||
|
||||
let incrementCount;
|
||||
|
||||
function Foo(props) {
|
||||
const context = React.useContext(MyContext);
|
||||
const [data, setData] = React.useState({count: context});
|
||||
|
||||
incrementCount = () => setData(({count}) => ({count: count + 1}));
|
||||
|
||||
return <div>count: {data.count}</div>;
|
||||
}
|
||||
|
||||
const renderer = ReactTestRenderer.create(<Foo />);
|
||||
expect(renderer.toJSON()).toEqual({
|
||||
type: 'div',
|
||||
props: {},
|
||||
children: ['count: ', '1'],
|
||||
});
|
||||
|
||||
act(incrementCount);
|
||||
expect(renderer.toJSON()).toEqual({
|
||||
type: 'div',
|
||||
props: {},
|
||||
children: ['count: ', '2'],
|
||||
});
|
||||
|
||||
const childFiber = renderer.root._currentFiber();
|
||||
const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
expect(tree).toEqual([
|
||||
{name: 'Context', value: 1, subHooks: []},
|
||||
{name: 'State', value: {count: 2}, subHooks: []},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "16.8.2",
|
||||
"version": "16.8.4",
|
||||
"description": "React package for working with the DOM.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -20,7 +20,7 @@
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.13.2"
|
||||
"scheduler": "^0.13.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0"
|
||||
|
||||
@@ -482,5 +482,98 @@ describe('ReactDOMServerIntegration', () => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/facebook/react/issues/14705
|
||||
it('does not pollute later renders when stream destroyed', () => {
|
||||
const LoggedInUser = React.createContext('default');
|
||||
|
||||
const AppWithUser = user => (
|
||||
<LoggedInUser.Provider value={user}>
|
||||
<header>
|
||||
<LoggedInUser.Consumer>{whoAmI => whoAmI}</LoggedInUser.Consumer>
|
||||
</header>
|
||||
</LoggedInUser.Provider>
|
||||
);
|
||||
|
||||
const stream = ReactDOMServer.renderToNodeStream(
|
||||
AppWithUser('Amy'),
|
||||
).setEncoding('utf8');
|
||||
|
||||
// This is an implementation detail because we test a memory leak
|
||||
const {threadID} = stream.partialRenderer;
|
||||
|
||||
// Read enough to render Provider but not enough for it to be exited
|
||||
stream._read(10);
|
||||
expect(LoggedInUser[threadID]).toBe('Amy');
|
||||
|
||||
stream.destroy();
|
||||
|
||||
const AppWithUserNoProvider = () => (
|
||||
<LoggedInUser.Consumer>{whoAmI => whoAmI}</LoggedInUser.Consumer>
|
||||
);
|
||||
|
||||
const stream2 = ReactDOMServer.renderToNodeStream(
|
||||
AppWithUserNoProvider(),
|
||||
).setEncoding('utf8');
|
||||
|
||||
// Sanity check to ensure 2nd render has same threadID as 1st render,
|
||||
// otherwise this test is not testing what it's meant to
|
||||
expect(stream2.partialRenderer.threadID).toBe(threadID);
|
||||
|
||||
const markup = stream2.read(Infinity);
|
||||
|
||||
expect(markup).toBe('default');
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/facebook/react/issues/14705
|
||||
it('frees context value reference when stream destroyed', () => {
|
||||
const LoggedInUser = React.createContext('default');
|
||||
|
||||
const AppWithUser = user => (
|
||||
<LoggedInUser.Provider value={user}>
|
||||
<header>
|
||||
<LoggedInUser.Consumer>{whoAmI => whoAmI}</LoggedInUser.Consumer>
|
||||
</header>
|
||||
</LoggedInUser.Provider>
|
||||
);
|
||||
|
||||
const stream = ReactDOMServer.renderToNodeStream(
|
||||
AppWithUser('Amy'),
|
||||
).setEncoding('utf8');
|
||||
|
||||
// This is an implementation detail because we test a memory leak
|
||||
const {threadID} = stream.partialRenderer;
|
||||
|
||||
// Read enough to render Provider but not enough for it to be exited
|
||||
stream._read(10);
|
||||
expect(LoggedInUser[threadID]).toBe('Amy');
|
||||
|
||||
stream.destroy();
|
||||
expect(LoggedInUser[threadID]).toBe('default');
|
||||
});
|
||||
|
||||
it('does not pollute sync renders after an error', () => {
|
||||
const LoggedInUser = React.createContext('default');
|
||||
const Crash = () => {
|
||||
throw new Error('Boo!');
|
||||
};
|
||||
const AppWithUser = user => (
|
||||
<LoggedInUser.Provider value={user}>
|
||||
<LoggedInUser.Consumer>{whoAmI => whoAmI}</LoggedInUser.Consumer>
|
||||
<Crash />
|
||||
</LoggedInUser.Provider>
|
||||
);
|
||||
|
||||
expect(() => {
|
||||
ReactDOMServer.renderToString(AppWithUser('Casper'));
|
||||
}).toThrow('Boo');
|
||||
|
||||
// Should not report a value from failed render
|
||||
expect(
|
||||
ReactDOMServer.renderToString(
|
||||
<LoggedInUser.Consumer>{whoAmI => whoAmI}</LoggedInUser.Consumer>,
|
||||
),
|
||||
).toBe('default');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -715,6 +715,7 @@ class ReactDOMServerRenderer {
|
||||
destroy() {
|
||||
if (!this.exhausted) {
|
||||
this.exhausted = true;
|
||||
this.clearProviders();
|
||||
freeThreadID(this.threadID);
|
||||
}
|
||||
}
|
||||
@@ -776,6 +777,15 @@ class ReactDOMServerRenderer {
|
||||
context[this.threadID] = previousValue;
|
||||
}
|
||||
|
||||
clearProviders(): void {
|
||||
// Restore any remaining providers on the stack to previous values
|
||||
for (let index = this.contextIndex; index >= 0; index--) {
|
||||
const context: ReactContext<any> = this.contextStack[index];
|
||||
const previousValue = this.contextValueStack[index];
|
||||
context[this.threadID] = previousValue;
|
||||
}
|
||||
}
|
||||
|
||||
read(bytes: number): string | null {
|
||||
if (this.exhausted) {
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-is",
|
||||
"version": "16.8.2",
|
||||
"version": "16.8.4",
|
||||
"description": "Brand checking of React Elements.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-reconciler",
|
||||
"description": "React package for creating custom renderers.",
|
||||
"version": "0.20.0",
|
||||
"version": "0.20.2",
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -33,7 +33,7 @@
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.13.2"
|
||||
"scheduler": "^0.13.4"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
|
||||
7
packages/react-reconciler/src/ReactFiber.js
vendored
7
packages/react-reconciler/src/ReactFiber.js
vendored
@@ -15,6 +15,7 @@ import type {SideEffectTag} from 'shared/ReactSideEffectTags';
|
||||
import type {ExpirationTime} from './ReactFiberExpirationTime';
|
||||
import type {UpdateQueue} from './ReactUpdateQueue';
|
||||
import type {ContextDependencyList} from './ReactFiberNewContext';
|
||||
import type {HookType} from './ReactFiberHooks';
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
@@ -204,6 +205,9 @@ export type Fiber = {|
|
||||
_debugSource?: Source | null,
|
||||
_debugOwner?: Fiber | null,
|
||||
_debugIsCurrentlyTiming?: boolean,
|
||||
|
||||
// Used to verify that the order of hooks does not change between renders.
|
||||
_debugHookTypes?: Array<HookType> | null,
|
||||
|};
|
||||
|
||||
let debugCounter;
|
||||
@@ -285,6 +289,7 @@ function FiberNode(
|
||||
this._debugSource = null;
|
||||
this._debugOwner = null;
|
||||
this._debugIsCurrentlyTiming = false;
|
||||
this._debugHookTypes = null;
|
||||
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
|
||||
Object.preventExtensions(this);
|
||||
}
|
||||
@@ -370,6 +375,7 @@ export function createWorkInProgress(
|
||||
workInProgress._debugID = current._debugID;
|
||||
workInProgress._debugSource = current._debugSource;
|
||||
workInProgress._debugOwner = current._debugOwner;
|
||||
workInProgress._debugHookTypes = current._debugHookTypes;
|
||||
}
|
||||
|
||||
workInProgress.alternate = current;
|
||||
@@ -723,5 +729,6 @@ export function assignFiberPropertiesInDEV(
|
||||
target._debugSource = source._debugSource;
|
||||
target._debugOwner = source._debugOwner;
|
||||
target._debugIsCurrentlyTiming = source._debugIsCurrentlyTiming;
|
||||
target._debugHookTypes = source._debugHookTypes;
|
||||
return target;
|
||||
}
|
||||
|
||||
355
packages/react-reconciler/src/ReactFiberHooks.js
vendored
355
packages/react-reconciler/src/ReactFiberHooks.js
vendored
@@ -93,7 +93,7 @@ type UpdateQueue<S, A> = {
|
||||
eagerState: S | null,
|
||||
};
|
||||
|
||||
type HookType =
|
||||
export type HookType =
|
||||
| 'useState'
|
||||
| 'useReducer'
|
||||
| 'useContext'
|
||||
@@ -120,10 +120,6 @@ export type Hook = {
|
||||
next: Hook | null,
|
||||
};
|
||||
|
||||
type HookDev = Hook & {
|
||||
_debugType: HookType,
|
||||
};
|
||||
|
||||
type Effect = {
|
||||
tag: HookEffectTag,
|
||||
create: () => (() => void) | void,
|
||||
@@ -150,7 +146,6 @@ let currentlyRenderingFiber: Fiber | null = null;
|
||||
// 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.
|
||||
let firstCurrentHook: Hook | null = null;
|
||||
let currentHook: Hook | null = null;
|
||||
let nextCurrentHook: Hook | null = null;
|
||||
let firstWorkInProgressHook: Hook | null = null;
|
||||
@@ -183,7 +178,38 @@ const RE_RENDER_LIMIT = 25;
|
||||
// In DEV, this is the name of the currently executing primitive hook
|
||||
let currentHookNameInDev: ?HookType = 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.
|
||||
let hookTypesDev: Array<HookType> | null = null;
|
||||
let hookTypesUpdateIndexDev: number = -1;
|
||||
|
||||
function mountHookTypesDev() {
|
||||
if (__DEV__) {
|
||||
const hookName = ((currentHookNameInDev: any): HookType);
|
||||
|
||||
if (hookTypesDev === null) {
|
||||
hookTypesDev = [hookName];
|
||||
} else {
|
||||
hookTypesDev.push(hookName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateHookTypesDev() {
|
||||
if (__DEV__) {
|
||||
const hookName = ((currentHookNameInDev: any): HookType);
|
||||
|
||||
if (hookTypesDev !== null) {
|
||||
hookTypesUpdateIndexDev++;
|
||||
if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
|
||||
warnOnHookMismatchInDev(hookName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function warnOnHookMismatchInDev(currentHookName: HookType) {
|
||||
if (__DEV__) {
|
||||
const componentName = getComponentName(
|
||||
((currentlyRenderingFiber: any): Fiber).type,
|
||||
@@ -191,44 +217,44 @@ function warnOnHookMismatchInDev() {
|
||||
if (!didWarnAboutMismatchedHooksForComponent.has(componentName)) {
|
||||
didWarnAboutMismatchedHooksForComponent.add(componentName);
|
||||
|
||||
const secondColumnStart = 22;
|
||||
if (hookTypesDev !== null) {
|
||||
let table = '';
|
||||
|
||||
let table = '';
|
||||
let prevHook: HookDev | null = (firstCurrentHook: any);
|
||||
let nextHook: HookDev | null = (firstWorkInProgressHook: any);
|
||||
let n = 1;
|
||||
while (prevHook !== null && nextHook !== null) {
|
||||
const oldHookName = prevHook._debugType;
|
||||
const newHookName = nextHook._debugType;
|
||||
const secondColumnStart = 30;
|
||||
|
||||
let row = `${n}. ${oldHookName}`;
|
||||
for (let i = 0; i <= ((hookTypesUpdateIndexDev: any): number); i++) {
|
||||
const oldHookName = hookTypesDev[i];
|
||||
const newHookName =
|
||||
i === ((hookTypesUpdateIndexDev: any): number)
|
||||
? currentHookName
|
||||
: oldHookName;
|
||||
|
||||
// Extra space so second column lines up
|
||||
// lol @ IE not supporting String#repeat
|
||||
while (row.length < secondColumnStart) {
|
||||
row += ' ';
|
||||
let row = `${i + 1}. ${oldHookName}`;
|
||||
|
||||
// Extra space so second column lines up
|
||||
// lol @ IE not supporting String#repeat
|
||||
while (row.length < secondColumnStart) {
|
||||
row += ' ';
|
||||
}
|
||||
|
||||
row += newHookName + '\n';
|
||||
|
||||
table += row;
|
||||
}
|
||||
|
||||
row += newHookName + '\n';
|
||||
|
||||
table += row;
|
||||
prevHook = (prevHook.next: any);
|
||||
nextHook = (nextHook.next: any);
|
||||
n++;
|
||||
warning(
|
||||
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(
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,8 +319,15 @@ export function renderWithHooks(
|
||||
): any {
|
||||
renderExpirationTime = nextRenderExpirationTime;
|
||||
currentlyRenderingFiber = workInProgress;
|
||||
firstCurrentHook = nextCurrentHook =
|
||||
current !== null ? current.memoizedState : null;
|
||||
nextCurrentHook = current !== null ? current.memoizedState : null;
|
||||
|
||||
if (__DEV__) {
|
||||
hookTypesDev =
|
||||
current !== null
|
||||
? ((current._debugHookTypes: any): Array<HookType>)
|
||||
: null;
|
||||
hookTypesUpdateIndexDev = -1;
|
||||
}
|
||||
|
||||
// The following should have already been reset
|
||||
// currentHook = null;
|
||||
@@ -308,11 +341,26 @@ export function renderWithHooks(
|
||||
// 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.
|
||||
if (__DEV__) {
|
||||
ReactCurrentDispatcher.current =
|
||||
nextCurrentHook === null
|
||||
? HooksDispatcherOnMountInDEV
|
||||
: HooksDispatcherOnUpdateInDEV;
|
||||
if (nextCurrentHook !== null) {
|
||||
ReactCurrentDispatcher.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.current = HooksDispatcherOnMountWithHookTypesInDEV;
|
||||
} else {
|
||||
ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
|
||||
}
|
||||
} else {
|
||||
ReactCurrentDispatcher.current =
|
||||
nextCurrentHook === null
|
||||
@@ -328,14 +376,18 @@ export function renderWithHooks(
|
||||
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;
|
||||
|
||||
if (__DEV__) {
|
||||
// Also validate hook order for cascading updates.
|
||||
hookTypesUpdateIndexDev = -1;
|
||||
}
|
||||
|
||||
ReactCurrentDispatcher.current = __DEV__
|
||||
? HooksDispatcherOnUpdateInDEV
|
||||
: HooksDispatcherOnUpdate;
|
||||
@@ -347,10 +399,6 @@ export function renderWithHooks(
|
||||
numberOfReRenders = 0;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
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.current = ContextOnlyDispatcher;
|
||||
@@ -362,19 +410,30 @@ export function renderWithHooks(
|
||||
renderedWork.updateQueue = (componentUpdateQueue: any);
|
||||
renderedWork.effectTag |= sideEffectTag;
|
||||
|
||||
if (__DEV__) {
|
||||
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.
|
||||
const didRenderTooFewHooks =
|
||||
currentHook !== null && currentHook.next !== null;
|
||||
|
||||
renderExpirationTime = NoWork;
|
||||
currentlyRenderingFiber = null;
|
||||
|
||||
firstCurrentHook = null;
|
||||
currentHook = null;
|
||||
nextCurrentHook = null;
|
||||
firstWorkInProgressHook = null;
|
||||
workInProgressHook = null;
|
||||
nextWorkInProgressHook = null;
|
||||
|
||||
if (__DEV__) {
|
||||
currentHookNameInDev = null;
|
||||
hookTypesDev = null;
|
||||
hookTypesUpdateIndexDev = -1;
|
||||
}
|
||||
|
||||
remainingExpirationTime = NoWork;
|
||||
componentUpdateQueue = null;
|
||||
sideEffectTag = 0;
|
||||
@@ -416,21 +475,23 @@ export function resetHooks(): void {
|
||||
renderExpirationTime = NoWork;
|
||||
currentlyRenderingFiber = null;
|
||||
|
||||
firstCurrentHook = null;
|
||||
currentHook = null;
|
||||
nextCurrentHook = null;
|
||||
firstWorkInProgressHook = null;
|
||||
workInProgressHook = null;
|
||||
nextWorkInProgressHook = null;
|
||||
|
||||
if (__DEV__) {
|
||||
hookTypesDev = null;
|
||||
hookTypesUpdateIndexDev = -1;
|
||||
|
||||
currentHookNameInDev = null;
|
||||
}
|
||||
|
||||
remainingExpirationTime = NoWork;
|
||||
componentUpdateQueue = null;
|
||||
sideEffectTag = 0;
|
||||
|
||||
if (__DEV__) {
|
||||
currentHookNameInDev = null;
|
||||
}
|
||||
|
||||
didScheduleRenderPhaseUpdate = false;
|
||||
renderPhaseUpdates = null;
|
||||
numberOfReRenders = 0;
|
||||
@@ -447,9 +508,6 @@ function mountWorkInProgressHook(): Hook {
|
||||
next: null,
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
(hook: any)._debugType = (currentHookNameInDev: any);
|
||||
}
|
||||
if (workInProgressHook === null) {
|
||||
// This is the first hook in the list
|
||||
firstWorkInProgressHook = workInProgressHook = hook;
|
||||
@@ -499,13 +557,6 @@ function updateWorkInProgressHook(): Hook {
|
||||
workInProgressHook = workInProgressHook.next = newHook;
|
||||
}
|
||||
nextCurrentHook = currentHook.next;
|
||||
|
||||
if (__DEV__) {
|
||||
(newHook: any)._debugType = (currentHookNameInDev: any);
|
||||
if (currentHookNameInDev !== ((currentHook: any): HookDev)._debugType) {
|
||||
warnOnHookMismatchInDev();
|
||||
}
|
||||
}
|
||||
}
|
||||
return workInProgressHook;
|
||||
}
|
||||
@@ -520,26 +571,6 @@ function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
|
||||
return typeof action === 'function' ? action(state) : action;
|
||||
}
|
||||
|
||||
function mountContext<T>(
|
||||
context: ReactContext<T>,
|
||||
observedBits: void | number | boolean,
|
||||
): T {
|
||||
if (__DEV__) {
|
||||
mountWorkInProgressHook();
|
||||
}
|
||||
return readContext(context, observedBits);
|
||||
}
|
||||
|
||||
function updateContext<T>(
|
||||
context: ReactContext<T>,
|
||||
observedBits: void | number | boolean,
|
||||
): T {
|
||||
if (__DEV__) {
|
||||
updateWorkInProgressHook();
|
||||
}
|
||||
return readContext(context, observedBits);
|
||||
}
|
||||
|
||||
function mountReducer<S, I, A>(
|
||||
reducer: (S, A) => S,
|
||||
initialArg: I,
|
||||
@@ -607,7 +638,6 @@ function updateReducer<S, I, A>(
|
||||
}
|
||||
|
||||
hook.memoizedState = newState;
|
||||
|
||||
// Don't persist the state accumlated from the render phase updates to
|
||||
// the base state unless the queue is empty.
|
||||
// TODO: Not sure if this is the desired semantics, but it's what we
|
||||
@@ -616,6 +646,9 @@ function updateReducer<S, I, A>(
|
||||
hook.baseState = newState;
|
||||
}
|
||||
|
||||
queue.eagerReducer = reducer;
|
||||
queue.eagerState = newState;
|
||||
|
||||
return [newState, dispatch];
|
||||
}
|
||||
}
|
||||
@@ -1179,6 +1212,7 @@ const HooksDispatcherOnUpdate: Dispatcher = {
|
||||
};
|
||||
|
||||
let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
|
||||
let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null;
|
||||
let HooksDispatcherOnUpdateInDEV: Dispatcher | null = null;
|
||||
let InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher | null = null;
|
||||
let InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher | null = null;
|
||||
@@ -1214,6 +1248,7 @@ if (__DEV__) {
|
||||
|
||||
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useCallback';
|
||||
mountHookTypesDev();
|
||||
return mountCallback(callback, deps);
|
||||
},
|
||||
useContext<T>(
|
||||
@@ -1221,13 +1256,15 @@ if (__DEV__) {
|
||||
observedBits: void | number | boolean,
|
||||
): T {
|
||||
currentHookNameInDev = 'useContext';
|
||||
return mountContext(context, observedBits);
|
||||
mountHookTypesDev();
|
||||
return readContext(context, observedBits);
|
||||
},
|
||||
useEffect(
|
||||
create: () => (() => void) | void,
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useEffect';
|
||||
mountHookTypesDev();
|
||||
return mountEffect(create, deps);
|
||||
},
|
||||
useImperativeHandle<T>(
|
||||
@@ -1236,6 +1273,7 @@ if (__DEV__) {
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useImperativeHandle';
|
||||
mountHookTypesDev();
|
||||
return mountImperativeHandle(ref, create, deps);
|
||||
},
|
||||
useLayoutEffect(
|
||||
@@ -1243,10 +1281,12 @@ if (__DEV__) {
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useLayoutEffect';
|
||||
mountHookTypesDev();
|
||||
return mountLayoutEffect(create, deps);
|
||||
},
|
||||
useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useMemo';
|
||||
mountHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
@@ -1261,6 +1301,7 @@ if (__DEV__) {
|
||||
init?: I => S,
|
||||
): [S, Dispatch<A>] {
|
||||
currentHookNameInDev = 'useReducer';
|
||||
mountHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
@@ -1271,12 +1312,14 @@ if (__DEV__) {
|
||||
},
|
||||
useRef<T>(initialValue: T): {current: T} {
|
||||
currentHookNameInDev = 'useRef';
|
||||
mountHookTypesDev();
|
||||
return mountRef(initialValue);
|
||||
},
|
||||
useState<S>(
|
||||
initialState: (() => S) | S,
|
||||
): [S, Dispatch<BasicStateAction<S>>] {
|
||||
currentHookNameInDev = 'useState';
|
||||
mountHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
@@ -1287,6 +1330,104 @@ if (__DEV__) {
|
||||
},
|
||||
useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
|
||||
currentHookNameInDev = 'useDebugValue';
|
||||
mountHookTypesDev();
|
||||
return mountDebugValue(value, formatterFn);
|
||||
},
|
||||
};
|
||||
|
||||
HooksDispatcherOnMountWithHookTypesInDEV = {
|
||||
readContext<T>(
|
||||
context: ReactContext<T>,
|
||||
observedBits: void | number | boolean,
|
||||
): T {
|
||||
return readContext(context, observedBits);
|
||||
},
|
||||
|
||||
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useCallback';
|
||||
updateHookTypesDev();
|
||||
return mountCallback(callback, deps);
|
||||
},
|
||||
useContext<T>(
|
||||
context: ReactContext<T>,
|
||||
observedBits: void | number | boolean,
|
||||
): T {
|
||||
currentHookNameInDev = 'useContext';
|
||||
updateHookTypesDev();
|
||||
return readContext(context, observedBits);
|
||||
},
|
||||
useEffect(
|
||||
create: () => (() => void) | void,
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useEffect';
|
||||
updateHookTypesDev();
|
||||
return mountEffect(create, deps);
|
||||
},
|
||||
useImperativeHandle<T>(
|
||||
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
|
||||
create: () => T,
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useImperativeHandle';
|
||||
updateHookTypesDev();
|
||||
return mountImperativeHandle(ref, create, deps);
|
||||
},
|
||||
useLayoutEffect(
|
||||
create: () => (() => void) | void,
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useLayoutEffect';
|
||||
updateHookTypesDev();
|
||||
return mountLayoutEffect(create, deps);
|
||||
},
|
||||
useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useMemo';
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
return mountMemo(create, deps);
|
||||
} finally {
|
||||
ReactCurrentDispatcher.current = prevDispatcher;
|
||||
}
|
||||
},
|
||||
useReducer<S, I, A>(
|
||||
reducer: (S, A) => S,
|
||||
initialArg: I,
|
||||
init?: I => S,
|
||||
): [S, Dispatch<A>] {
|
||||
currentHookNameInDev = 'useReducer';
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
return mountReducer(reducer, initialArg, init);
|
||||
} finally {
|
||||
ReactCurrentDispatcher.current = prevDispatcher;
|
||||
}
|
||||
},
|
||||
useRef<T>(initialValue: T): {current: T} {
|
||||
currentHookNameInDev = 'useRef';
|
||||
updateHookTypesDev();
|
||||
return mountRef(initialValue);
|
||||
},
|
||||
useState<S>(
|
||||
initialState: (() => S) | S,
|
||||
): [S, Dispatch<BasicStateAction<S>>] {
|
||||
currentHookNameInDev = 'useState';
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
return mountState(initialState);
|
||||
} finally {
|
||||
ReactCurrentDispatcher.current = prevDispatcher;
|
||||
}
|
||||
},
|
||||
useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
|
||||
currentHookNameInDev = 'useDebugValue';
|
||||
updateHookTypesDev();
|
||||
return mountDebugValue(value, formatterFn);
|
||||
},
|
||||
};
|
||||
@@ -1301,6 +1442,7 @@ if (__DEV__) {
|
||||
|
||||
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useCallback';
|
||||
updateHookTypesDev();
|
||||
return updateCallback(callback, deps);
|
||||
},
|
||||
useContext<T>(
|
||||
@@ -1308,13 +1450,15 @@ if (__DEV__) {
|
||||
observedBits: void | number | boolean,
|
||||
): T {
|
||||
currentHookNameInDev = 'useContext';
|
||||
return updateContext(context, observedBits);
|
||||
updateHookTypesDev();
|
||||
return readContext(context, observedBits);
|
||||
},
|
||||
useEffect(
|
||||
create: () => (() => void) | void,
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useEffect';
|
||||
updateHookTypesDev();
|
||||
return updateEffect(create, deps);
|
||||
},
|
||||
useImperativeHandle<T>(
|
||||
@@ -1323,6 +1467,7 @@ if (__DEV__) {
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useImperativeHandle';
|
||||
updateHookTypesDev();
|
||||
return updateImperativeHandle(ref, create, deps);
|
||||
},
|
||||
useLayoutEffect(
|
||||
@@ -1330,10 +1475,12 @@ if (__DEV__) {
|
||||
deps: Array<mixed> | void | null,
|
||||
): void {
|
||||
currentHookNameInDev = 'useLayoutEffect';
|
||||
updateHookTypesDev();
|
||||
return updateLayoutEffect(create, deps);
|
||||
},
|
||||
useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useMemo';
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||||
try {
|
||||
@@ -1348,6 +1495,7 @@ if (__DEV__) {
|
||||
init?: I => S,
|
||||
): [S, Dispatch<A>] {
|
||||
currentHookNameInDev = 'useReducer';
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||||
try {
|
||||
@@ -1358,12 +1506,14 @@ if (__DEV__) {
|
||||
},
|
||||
useRef<T>(initialValue: T): {current: T} {
|
||||
currentHookNameInDev = 'useRef';
|
||||
updateHookTypesDev();
|
||||
return updateRef(initialValue);
|
||||
},
|
||||
useState<S>(
|
||||
initialState: (() => S) | S,
|
||||
): [S, Dispatch<BasicStateAction<S>>] {
|
||||
currentHookNameInDev = 'useState';
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||||
try {
|
||||
@@ -1374,6 +1524,7 @@ if (__DEV__) {
|
||||
},
|
||||
useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
|
||||
currentHookNameInDev = 'useDebugValue';
|
||||
updateHookTypesDev();
|
||||
return updateDebugValue(value, formatterFn);
|
||||
},
|
||||
};
|
||||
@@ -1390,6 +1541,7 @@ if (__DEV__) {
|
||||
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useCallback';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
return mountCallback(callback, deps);
|
||||
},
|
||||
useContext<T>(
|
||||
@@ -1398,7 +1550,8 @@ if (__DEV__) {
|
||||
): T {
|
||||
currentHookNameInDev = 'useContext';
|
||||
warnInvalidHookAccess();
|
||||
return mountContext(context, observedBits);
|
||||
mountHookTypesDev();
|
||||
return readContext(context, observedBits);
|
||||
},
|
||||
useEffect(
|
||||
create: () => (() => void) | void,
|
||||
@@ -1406,6 +1559,7 @@ if (__DEV__) {
|
||||
): void {
|
||||
currentHookNameInDev = 'useEffect';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
return mountEffect(create, deps);
|
||||
},
|
||||
useImperativeHandle<T>(
|
||||
@@ -1415,6 +1569,7 @@ if (__DEV__) {
|
||||
): void {
|
||||
currentHookNameInDev = 'useImperativeHandle';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
return mountImperativeHandle(ref, create, deps);
|
||||
},
|
||||
useLayoutEffect(
|
||||
@@ -1423,11 +1578,13 @@ if (__DEV__) {
|
||||
): void {
|
||||
currentHookNameInDev = 'useLayoutEffect';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
return mountLayoutEffect(create, deps);
|
||||
},
|
||||
useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useMemo';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
@@ -1443,6 +1600,7 @@ if (__DEV__) {
|
||||
): [S, Dispatch<A>] {
|
||||
currentHookNameInDev = 'useReducer';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
@@ -1454,6 +1612,7 @@ if (__DEV__) {
|
||||
useRef<T>(initialValue: T): {current: T} {
|
||||
currentHookNameInDev = 'useRef';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
return mountRef(initialValue);
|
||||
},
|
||||
useState<S>(
|
||||
@@ -1461,6 +1620,7 @@ if (__DEV__) {
|
||||
): [S, Dispatch<BasicStateAction<S>>] {
|
||||
currentHookNameInDev = 'useState';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||||
try {
|
||||
@@ -1472,6 +1632,7 @@ if (__DEV__) {
|
||||
useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
|
||||
currentHookNameInDev = 'useDebugValue';
|
||||
warnInvalidHookAccess();
|
||||
mountHookTypesDev();
|
||||
return mountDebugValue(value, formatterFn);
|
||||
},
|
||||
};
|
||||
@@ -1488,6 +1649,7 @@ if (__DEV__) {
|
||||
useCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useCallback';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
return updateCallback(callback, deps);
|
||||
},
|
||||
useContext<T>(
|
||||
@@ -1496,7 +1658,8 @@ if (__DEV__) {
|
||||
): T {
|
||||
currentHookNameInDev = 'useContext';
|
||||
warnInvalidHookAccess();
|
||||
return updateContext(context, observedBits);
|
||||
updateHookTypesDev();
|
||||
return readContext(context, observedBits);
|
||||
},
|
||||
useEffect(
|
||||
create: () => (() => void) | void,
|
||||
@@ -1504,6 +1667,7 @@ if (__DEV__) {
|
||||
): void {
|
||||
currentHookNameInDev = 'useEffect';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
return updateEffect(create, deps);
|
||||
},
|
||||
useImperativeHandle<T>(
|
||||
@@ -1513,6 +1677,7 @@ if (__DEV__) {
|
||||
): void {
|
||||
currentHookNameInDev = 'useImperativeHandle';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
return updateImperativeHandle(ref, create, deps);
|
||||
},
|
||||
useLayoutEffect(
|
||||
@@ -1521,11 +1686,13 @@ if (__DEV__) {
|
||||
): void {
|
||||
currentHookNameInDev = 'useLayoutEffect';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
return updateLayoutEffect(create, deps);
|
||||
},
|
||||
useMemo<T>(create: () => T, deps: Array<mixed> | void | null): T {
|
||||
currentHookNameInDev = 'useMemo';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||||
try {
|
||||
@@ -1541,6 +1708,7 @@ if (__DEV__) {
|
||||
): [S, Dispatch<A>] {
|
||||
currentHookNameInDev = 'useReducer';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||||
try {
|
||||
@@ -1552,6 +1720,7 @@ if (__DEV__) {
|
||||
useRef<T>(initialValue: T): {current: T} {
|
||||
currentHookNameInDev = 'useRef';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
return updateRef(initialValue);
|
||||
},
|
||||
useState<S>(
|
||||
@@ -1559,6 +1728,7 @@ if (__DEV__) {
|
||||
): [S, Dispatch<BasicStateAction<S>>] {
|
||||
currentHookNameInDev = 'useState';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||||
try {
|
||||
@@ -1570,6 +1740,7 @@ if (__DEV__) {
|
||||
useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void {
|
||||
currentHookNameInDev = 'useDebugValue';
|
||||
warnInvalidHookAccess();
|
||||
updateHookTypesDev();
|
||||
return updateDebugValue(value, formatterFn);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -669,6 +669,76 @@ describe('ReactHooks', () => {
|
||||
}).toThrow('is not a function');
|
||||
});
|
||||
|
||||
it('does not forget render phase useState updates inside an effect', () => {
|
||||
const {useState, useEffect} = React;
|
||||
|
||||
function Counter() {
|
||||
const [counter, setCounter] = useState(0);
|
||||
if (counter === 0) {
|
||||
setCounter(x => x + 1);
|
||||
setCounter(x => x + 1);
|
||||
}
|
||||
useEffect(() => {
|
||||
setCounter(x => x + 1);
|
||||
setCounter(x => x + 1);
|
||||
}, []);
|
||||
return counter;
|
||||
}
|
||||
|
||||
const root = ReactTestRenderer.create(null);
|
||||
ReactTestRenderer.act(() => {
|
||||
root.update(<Counter />);
|
||||
});
|
||||
expect(root).toMatchRenderedOutput('4');
|
||||
});
|
||||
|
||||
it('does not forget render phase useReducer updates inside an effect with hoisted reducer', () => {
|
||||
const {useReducer, useEffect} = React;
|
||||
|
||||
const reducer = x => x + 1;
|
||||
function Counter() {
|
||||
const [counter, increment] = useReducer(reducer, 0);
|
||||
if (counter === 0) {
|
||||
increment();
|
||||
increment();
|
||||
}
|
||||
useEffect(() => {
|
||||
increment();
|
||||
increment();
|
||||
}, []);
|
||||
return counter;
|
||||
}
|
||||
|
||||
const root = ReactTestRenderer.create(null);
|
||||
ReactTestRenderer.act(() => {
|
||||
root.update(<Counter />);
|
||||
});
|
||||
expect(root).toMatchRenderedOutput('4');
|
||||
});
|
||||
|
||||
it('does not forget render phase useReducer updates inside an effect with inline reducer', () => {
|
||||
const {useReducer, useEffect} = React;
|
||||
|
||||
function Counter() {
|
||||
const [counter, increment] = useReducer(x => x + 1, 0);
|
||||
if (counter === 0) {
|
||||
increment();
|
||||
increment();
|
||||
}
|
||||
useEffect(() => {
|
||||
increment();
|
||||
increment();
|
||||
}, []);
|
||||
return counter;
|
||||
}
|
||||
|
||||
const root = ReactTestRenderer.create(null);
|
||||
ReactTestRenderer.act(() => {
|
||||
root.update(<Counter />);
|
||||
});
|
||||
expect(root).toMatchRenderedOutput('4');
|
||||
});
|
||||
|
||||
it('warns for bad useImperativeHandle first arg', () => {
|
||||
const {useImperativeHandle} = React;
|
||||
function App() {
|
||||
@@ -914,8 +984,6 @@ describe('ReactHooks', () => {
|
||||
it('warns when calling hooks inside useReducer', () => {
|
||||
const {useReducer, useState, useRef} = React;
|
||||
|
||||
spyOnDev(console, 'error');
|
||||
|
||||
function App() {
|
||||
const [value, dispatch] = useReducer((state, action) => {
|
||||
useRef(0);
|
||||
@@ -927,16 +995,23 @@ describe('ReactHooks', () => {
|
||||
useState();
|
||||
return value;
|
||||
}
|
||||
expect(() => {
|
||||
ReactTestRenderer.create(<App />);
|
||||
}).toThrow('Rendered more hooks than during the previous render.');
|
||||
|
||||
if (__DEV__) {
|
||||
expect(console.error).toHaveBeenCalledTimes(3);
|
||||
expect(console.error.calls.argsFor(0)[0]).toContain(
|
||||
'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
|
||||
);
|
||||
}
|
||||
expect(() => {
|
||||
expect(() => {
|
||||
ReactTestRenderer.create(<App />);
|
||||
}).toThrow('Rendered more hooks than during the previous render.');
|
||||
}).toWarnDev([
|
||||
'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
|
||||
'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks',
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
'1. useReducer useReducer\n' +
|
||||
'2. useState useRef\n' +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n',
|
||||
]);
|
||||
});
|
||||
|
||||
it("warns when calling hooks inside useState's initialize function", () => {
|
||||
@@ -1267,74 +1342,261 @@ describe('ReactHooks', () => {
|
||||
expect(useMemoCount).toBe(__DEV__ ? 2 : 1); // Has Hooks
|
||||
});
|
||||
|
||||
it('warns on using differently ordered hooks on subsequent renders', () => {
|
||||
const {useState, useReducer, useRef} = React;
|
||||
function useCustomHook() {
|
||||
return useState(0);
|
||||
}
|
||||
function App(props) {
|
||||
/* eslint-disable no-unused-vars */
|
||||
if (props.flip) {
|
||||
useCustomHook(0);
|
||||
useReducer((s, a) => a, 0);
|
||||
} else {
|
||||
useReducer((s, a) => a, 0);
|
||||
useCustomHook(0);
|
||||
}
|
||||
// This should not appear in the warning message because it occurs after
|
||||
// the first mismatch
|
||||
const ref = useRef(null);
|
||||
return null;
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
let root = ReactTestRenderer.create(<App flip={false} />);
|
||||
expect(() => {
|
||||
root.update(<App flip={true} />);
|
||||
}).toWarnDev([
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
'1. useReducer useState\n' +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n' +
|
||||
' in App (at **)',
|
||||
]);
|
||||
describe('hook ordering', () => {
|
||||
const useCallbackHelper = () => React.useCallback(() => {}, []);
|
||||
const useContextHelper = () => React.useContext(React.createContext());
|
||||
const useDebugValueHelper = () => React.useDebugValue('abc');
|
||||
const useEffectHelper = () => React.useEffect(() => () => {}, []);
|
||||
const useImperativeHandleHelper = () => {
|
||||
React.useImperativeHandle({current: null}, () => ({}), []);
|
||||
};
|
||||
const useLayoutEffectHelper = () =>
|
||||
React.useLayoutEffect(() => () => {}, []);
|
||||
const useMemoHelper = () => React.useMemo(() => 123, []);
|
||||
const useReducerHelper = () => React.useReducer((s, a) => a, 0);
|
||||
const useRefHelper = () => React.useRef(null);
|
||||
const useStateHelper = () => React.useState(0);
|
||||
|
||||
// further warnings for this component are silenced
|
||||
root.update(<App flip={false} />);
|
||||
});
|
||||
// We don't include useImperativeHandleHelper in this set,
|
||||
// because it generates an additional warning about the inputs length changing.
|
||||
// We test it below with its own test.
|
||||
let orderedHooks = [
|
||||
useCallbackHelper,
|
||||
useContextHelper,
|
||||
useDebugValueHelper,
|
||||
useEffectHelper,
|
||||
useLayoutEffectHelper,
|
||||
useMemoHelper,
|
||||
useReducerHelper,
|
||||
useRefHelper,
|
||||
useStateHelper,
|
||||
];
|
||||
|
||||
it('detects a bad hook order even if the component throws', () => {
|
||||
const {useState, useReducer} = React;
|
||||
function useCustomHook() {
|
||||
useState(0);
|
||||
}
|
||||
function App(props) {
|
||||
/* eslint-disable no-unused-vars */
|
||||
if (props.flip) {
|
||||
useCustomHook();
|
||||
useReducer((s, a) => a, 0);
|
||||
throw new Error('custom error');
|
||||
} else {
|
||||
useReducer((s, a) => a, 0);
|
||||
useCustomHook();
|
||||
const formatHookNamesToMatchErrorMessage = (hookNameA, hookNameB) => {
|
||||
return `use${hookNameA}${' '.repeat(24 - hookNameA.length)}${
|
||||
hookNameB ? `use${hookNameB}` : undefined
|
||||
}`;
|
||||
};
|
||||
|
||||
orderedHooks.forEach((firstHelper, index) => {
|
||||
const secondHelper =
|
||||
index > 0
|
||||
? orderedHooks[index - 1]
|
||||
: orderedHooks[orderedHooks.length - 1];
|
||||
|
||||
const hookNameA = firstHelper.name
|
||||
.replace('use', '')
|
||||
.replace('Helper', '');
|
||||
const hookNameB = secondHelper.name
|
||||
.replace('use', '')
|
||||
.replace('Helper', '');
|
||||
|
||||
it(`warns on using differently ordered hooks (${hookNameA}, ${hookNameB}) on subsequent renders`, () => {
|
||||
function App(props) {
|
||||
/* eslint-disable no-unused-vars */
|
||||
if (props.update) {
|
||||
secondHelper();
|
||||
firstHelper();
|
||||
} else {
|
||||
firstHelper();
|
||||
secondHelper();
|
||||
}
|
||||
// This should not appear in the warning message because it occurs after the first mismatch
|
||||
useRefHelper();
|
||||
return null;
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
let root = ReactTestRenderer.create(<App update={false} />);
|
||||
expect(() => {
|
||||
try {
|
||||
root.update(<App update={true} />);
|
||||
} catch (error) {
|
||||
// Swapping certain types of hooks will cause runtime errors.
|
||||
// This is okay as far as this test is concerned.
|
||||
// We just want to verify that warnings are always logged.
|
||||
}
|
||||
}).toWarnDev([
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
`1. ${formatHookNamesToMatchErrorMessage(hookNameA, hookNameB)}\n` +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n' +
|
||||
' in App (at **)',
|
||||
]);
|
||||
|
||||
// further warnings for this component are silenced
|
||||
try {
|
||||
root.update(<App update={false} />);
|
||||
} catch (error) {
|
||||
// Swapping certain types of hooks will cause runtime errors.
|
||||
// This is okay as far as this test is concerned.
|
||||
// We just want to verify that warnings are always logged.
|
||||
}
|
||||
});
|
||||
|
||||
it(`warns when more hooks (${(hookNameA,
|
||||
hookNameB)}) are used during update than mount`, () => {
|
||||
function App(props) {
|
||||
/* eslint-disable no-unused-vars */
|
||||
if (props.update) {
|
||||
firstHelper();
|
||||
secondHelper();
|
||||
} else {
|
||||
firstHelper();
|
||||
}
|
||||
return null;
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
let root = ReactTestRenderer.create(<App update={false} />);
|
||||
expect(() => {
|
||||
try {
|
||||
root.update(<App update={true} />);
|
||||
} catch (error) {
|
||||
// Swapping certain types of hooks will cause runtime errors.
|
||||
// This is okay as far as this test is concerned.
|
||||
// We just want to verify that warnings are always logged.
|
||||
}
|
||||
}).toWarnDev([
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
`1. ${formatHookNamesToMatchErrorMessage(hookNameA, hookNameA)}\n` +
|
||||
`2. undefined use${hookNameB}\n` +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n' +
|
||||
' in App (at **)',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// We don't include useContext or useDebugValue in this set,
|
||||
// because they aren't added to the hooks list and so won't throw.
|
||||
let hooksInList = [
|
||||
useCallbackHelper,
|
||||
useEffectHelper,
|
||||
useImperativeHandleHelper,
|
||||
useLayoutEffectHelper,
|
||||
useMemoHelper,
|
||||
useReducerHelper,
|
||||
useRefHelper,
|
||||
useStateHelper,
|
||||
];
|
||||
|
||||
hooksInList.forEach((firstHelper, index) => {
|
||||
const secondHelper =
|
||||
index > 0
|
||||
? hooksInList[index - 1]
|
||||
: hooksInList[hooksInList.length - 1];
|
||||
|
||||
const hookNameA = firstHelper.name
|
||||
.replace('use', '')
|
||||
.replace('Helper', '');
|
||||
const hookNameB = secondHelper.name
|
||||
.replace('use', '')
|
||||
.replace('Helper', '');
|
||||
|
||||
it(`warns when fewer hooks (${(hookNameA,
|
||||
hookNameB)}) are used during update than mount`, () => {
|
||||
function App(props) {
|
||||
/* eslint-disable no-unused-vars */
|
||||
if (props.update) {
|
||||
firstHelper();
|
||||
} else {
|
||||
firstHelper();
|
||||
secondHelper();
|
||||
}
|
||||
return null;
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
let root = ReactTestRenderer.create(<App update={false} />);
|
||||
expect(() => {
|
||||
root.update(<App update={true} />);
|
||||
}).toThrow('Rendered fewer hooks than expected.');
|
||||
});
|
||||
});
|
||||
|
||||
it(
|
||||
'warns on using differently ordered hooks ' +
|
||||
'(useImperativeHandleHelper, useMemoHelper) on subsequent renders',
|
||||
() => {
|
||||
function App(props) {
|
||||
/* eslint-disable no-unused-vars */
|
||||
if (props.update) {
|
||||
useMemoHelper();
|
||||
useImperativeHandleHelper();
|
||||
} else {
|
||||
useImperativeHandleHelper();
|
||||
useMemoHelper();
|
||||
}
|
||||
// This should not appear in the warning message because it occurs after the first mismatch
|
||||
useRefHelper();
|
||||
return null;
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
let root = ReactTestRenderer.create(<App update={false} />);
|
||||
expect(() => {
|
||||
try {
|
||||
root.update(<App update={true} />);
|
||||
} catch (error) {
|
||||
// Swapping certain types of hooks will cause runtime errors.
|
||||
// This is okay as far as this test is concerned.
|
||||
// We just want to verify that warnings are always logged.
|
||||
}
|
||||
}).toWarnDev([
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
`1. ${formatHookNamesToMatchErrorMessage(
|
||||
'ImperativeHandle',
|
||||
'Memo',
|
||||
)}\n` +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n' +
|
||||
' in App (at **)',
|
||||
]);
|
||||
|
||||
// further warnings for this component are silenced
|
||||
root.update(<App update={false} />);
|
||||
},
|
||||
);
|
||||
|
||||
it('detects a bad hook order even if the component throws', () => {
|
||||
const {useState, useReducer} = React;
|
||||
function useCustomHook() {
|
||||
useState(0);
|
||||
}
|
||||
return null;
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
let root = ReactTestRenderer.create(<App flip={false} />);
|
||||
expect(() => {
|
||||
expect(() => root.update(<App flip={true} />)).toThrow('custom error');
|
||||
}).toWarnDev([
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
'1. useReducer useState\n' +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^',
|
||||
]);
|
||||
function App(props) {
|
||||
/* eslint-disable no-unused-vars */
|
||||
if (props.update) {
|
||||
useCustomHook();
|
||||
useReducer((s, a) => a, 0);
|
||||
throw new Error('custom error');
|
||||
} else {
|
||||
useReducer((s, a) => a, 0);
|
||||
useCustomHook();
|
||||
}
|
||||
return null;
|
||||
/* eslint-enable no-unused-vars */
|
||||
}
|
||||
let root = ReactTestRenderer.create(<App update={false} />);
|
||||
expect(() => {
|
||||
expect(() => root.update(<App update={true} />)).toThrow(
|
||||
'custom error',
|
||||
);
|
||||
}).toWarnDev([
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
'1. useReducer useState\n' +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// Regression test for #14674
|
||||
|
||||
@@ -454,7 +454,9 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
|
||||
// Test that it works on update, too. This time the log is a bit different
|
||||
// because we started with reducerB instead of reducerA.
|
||||
counter.current.dispatch('reset');
|
||||
ReactNoop.act(() => {
|
||||
counter.current.dispatch('reset');
|
||||
});
|
||||
ReactNoop.render(<Counter ref={counter} />);
|
||||
expect(ReactNoop.flush()).toEqual([
|
||||
'Render: 0',
|
||||
@@ -1738,8 +1740,20 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
|
||||
ReactNoop.render(<App loadC={true} />);
|
||||
expect(() => {
|
||||
expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: 0']);
|
||||
}).toThrow('Rendered more hooks than during the previous render');
|
||||
expect(() => {
|
||||
expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: 0']);
|
||||
}).toThrow('Rendered more hooks than during the previous render');
|
||||
}).toWarnDev([
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
'1. useState useState\n' +
|
||||
'2. useState useState\n' +
|
||||
'3. undefined useState\n' +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n',
|
||||
]);
|
||||
|
||||
// Uncomment if/when we support this again
|
||||
// expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 0')]);
|
||||
@@ -1817,8 +1831,19 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
|
||||
ReactNoop.render(<App showMore={true} />);
|
||||
expect(() => {
|
||||
expect(ReactNoop.flush()).toEqual([]);
|
||||
}).toThrow('Rendered more hooks than during the previous render');
|
||||
expect(() => {
|
||||
expect(ReactNoop.flush()).toEqual([]);
|
||||
}).toThrow('Rendered more hooks than during the previous render');
|
||||
}).toWarnDev([
|
||||
'Warning: React has detected a change in the order of Hooks called by App. ' +
|
||||
'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' +
|
||||
'1. useEffect useEffect\n' +
|
||||
'2. undefined useEffect\n' +
|
||||
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n',
|
||||
]);
|
||||
|
||||
// Uncomment if/when we support this again
|
||||
// ReactNoop.flushPassiveEffects();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-test-renderer",
|
||||
"version": "16.8.2",
|
||||
"version": "16.8.4",
|
||||
"description": "React package for snapshot testing.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -21,8 +21,8 @@
|
||||
"dependencies": {
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-is": "^16.8.2",
|
||||
"scheduler": "^0.13.2"
|
||||
"react-is": "^16.8.4",
|
||||
"scheduler": "^0.13.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
"version": "16.8.2",
|
||||
"version": "16.8.4",
|
||||
"homepage": "https://reactjs.org/",
|
||||
"bugs": "https://github.com/facebook/react/issues",
|
||||
"license": "MIT",
|
||||
@@ -29,7 +29,7 @@
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.13.2"
|
||||
"scheduler": "^0.13.4"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
|
||||
@@ -18,6 +18,11 @@ import {
|
||||
unstable_continueExecution,
|
||||
unstable_wrapCallback,
|
||||
unstable_getCurrentPriorityLevel,
|
||||
unstable_IdlePriority,
|
||||
unstable_ImmediatePriority,
|
||||
unstable_LowPriority,
|
||||
unstable_NormalPriority,
|
||||
unstable_UserBlockingPriority,
|
||||
} from 'scheduler';
|
||||
import {
|
||||
__interactionsRef,
|
||||
@@ -60,6 +65,11 @@ if (__UMD__) {
|
||||
unstable_pauseExecution,
|
||||
unstable_continueExecution,
|
||||
unstable_getCurrentPriorityLevel,
|
||||
unstable_IdlePriority,
|
||||
unstable_ImmediatePriority,
|
||||
unstable_LowPriority,
|
||||
unstable_NormalPriority,
|
||||
unstable_UserBlockingPriority,
|
||||
},
|
||||
SchedulerTracing: {
|
||||
__interactionsRef,
|
||||
|
||||
@@ -108,5 +108,25 @@
|
||||
unstable_continueExecution: unstable_continueExecution,
|
||||
unstable_pauseExecution: unstable_pauseExecution,
|
||||
unstable_getFirstCallbackNode: unstable_getFirstCallbackNode,
|
||||
get unstable_IdlePriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_IdlePriority;
|
||||
},
|
||||
get unstable_ImmediatePriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_ImmediatePriority;
|
||||
},
|
||||
get unstable_LowPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_LowPriority;
|
||||
},
|
||||
get unstable_NormalPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_NormalPriority;
|
||||
},
|
||||
get unstable_UserBlockingPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_UserBlockingPriority;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,5 +102,25 @@
|
||||
unstable_continueExecution: unstable_continueExecution,
|
||||
unstable_pauseExecution: unstable_pauseExecution,
|
||||
unstable_getFirstCallbackNode: unstable_getFirstCallbackNode,
|
||||
get unstable_IdlePriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_IdlePriority;
|
||||
},
|
||||
get unstable_ImmediatePriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_ImmediatePriority;
|
||||
},
|
||||
get unstable_LowPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_LowPriority;
|
||||
},
|
||||
get unstable_NormalPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_NormalPriority;
|
||||
},
|
||||
get unstable_UserBlockingPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_UserBlockingPriority;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,5 +102,25 @@
|
||||
unstable_continueExecution: unstable_continueExecution,
|
||||
unstable_pauseExecution: unstable_pauseExecution,
|
||||
unstable_getFirstCallbackNode: unstable_getFirstCallbackNode,
|
||||
get unstable_IdlePriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_IdlePriority;
|
||||
},
|
||||
get unstable_ImmediatePriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_ImmediatePriority;
|
||||
},
|
||||
get unstable_LowPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_LowPriority;
|
||||
},
|
||||
get unstable_NormalPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_NormalPriority;
|
||||
},
|
||||
get unstable_UserBlockingPriority() {
|
||||
return global.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.Scheduler.unstable_UserBlockingPriority;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "scheduler",
|
||||
"version": "0.13.2",
|
||||
"version": "0.13.4",
|
||||
"description": "Cooperative scheduler for the browser environment.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -17,8 +17,17 @@ describe('Scheduling UMD bundle', () => {
|
||||
});
|
||||
|
||||
function filterPrivateKeys(name) {
|
||||
// TODO: Figure out how to forward priority levels.
|
||||
return !name.startsWith('_') && !name.endsWith('Priority');
|
||||
// Be very careful adding things to this whitelist!
|
||||
// It's easy to introduce bugs by doing it:
|
||||
// https://github.com/facebook/react/issues/14904
|
||||
switch (name) {
|
||||
case '__interactionsRef':
|
||||
case '__subscriberRef':
|
||||
// Don't forward these. (TODO: why?)
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function validateForwardedAPIs(api, forwardedAPIs) {
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
'use strict';
|
||||
|
||||
// TODO: this is special because it gets imported during build.
|
||||
module.exports = '16.8.2';
|
||||
module.exports = '16.8.4';
|
||||
|
||||
Reference in New Issue
Block a user