2016-10-12 01:43:47 +08:00
|
|
|
/**
|
2018-09-08 06:11:23 +08:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2016-10-12 01:43:47 +08:00
|
|
|
*
|
2017-09-25 04:48:13 +08:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-10-12 01:43:47 +08:00
|
|
|
*
|
|
|
|
|
* @flow
|
|
|
|
|
*/
|
|
|
|
|
|
Split cross-package types from implementation
Some of our internal reconciler types have leaked into other packages.
Usually, these types are treated as opaque; we don't read and write
to its fields. This is good.
However, the type is often passed back to a reconciler method. For
example, React DOM creates a FiberRoot with `createContainer`, then
passes that root to `updateContainer`. It doesn't do anything with the
root except pass it through, but because `updateContainer` expects a
full FiberRoot, React DOM is still coupled to all its fields.
I don't know if there's an idiomatic way to handle this in Flow. Opaque
types are simlar, but those only work within a single file. AFAIK,
there's no way to use a package as the boundary for opaqueness.
The immediate problem this presents is that the reconciler refactor will
involve changes to our internal data structures. I don't want to have to
fork every single package that happens to pass through a Fiber or
FiberRoot, or access any one of its fields. So my current plan is to
share the same Flow type across both forks. The shared type will be a
superset of each implementation's type, e.g. Fiber will have both an
`expirationTime` field and a `lanes` field. The implementations will
diverge, but not the types.
To do this, I lifted the type definitions into a separate module.
2020-04-09 14:48:24 +08:00
|
|
|
import type {Fiber} from './ReactInternalTypes';
|
2020-04-10 03:51:02 +08:00
|
|
|
import type {ExpirationTime} from './ReactFiberExpirationTime.old';
|
2020-04-09 10:44:52 +08:00
|
|
|
import type {UpdateQueue} from './ReactUpdateQueue.old';
|
2020-04-16 10:10:15 +08:00
|
|
|
import type {ReactPriorityLevel} from './ReactInternalTypes';
|
2016-10-12 01:43:47 +08:00
|
|
|
|
2020-02-22 11:45:20 +08:00
|
|
|
import * as React from 'react';
|
2020-03-22 06:22:01 +08:00
|
|
|
import {Update, Snapshot} from './ReactSideEffectTags';
|
2017-11-18 02:49:54 +08:00
|
|
|
import {
|
2018-01-26 06:30:53 +08:00
|
|
|
debugRenderPhaseSideEffectsForStrictMode,
|
2019-08-02 08:21:32 +08:00
|
|
|
disableLegacyContext,
|
2020-04-16 10:10:15 +08:00
|
|
|
enableDebugTracing,
|
2018-01-20 01:36:46 +08:00
|
|
|
warnAboutDeprecatedLifecycles,
|
2017-11-18 02:49:54 +08:00
|
|
|
} from 'shared/ReactFeatureFlags';
|
2020-04-09 10:44:52 +08:00
|
|
|
import ReactStrictModeWarnings from './ReactStrictModeWarnings.old';
|
|
|
|
|
import {isMounted} from './ReactFiberTreeReflection';
|
2018-11-20 07:32:54 +08:00
|
|
|
import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap';
|
2018-06-16 01:12:45 +08:00
|
|
|
import shallowEqual from 'shared/shallowEqual';
|
2017-11-03 03:50:03 +08:00
|
|
|
import getComponentName from 'shared/getComponentName';
|
2018-06-19 23:03:45 +08:00
|
|
|
import invariant from 'shared/invariant';
|
2019-03-19 21:31:26 +08:00
|
|
|
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
|
2017-11-03 03:50:03 +08:00
|
|
|
|
2020-04-09 10:44:52 +08:00
|
|
|
import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
|
2020-04-16 10:10:15 +08:00
|
|
|
import {DebugTracingMode, StrictMode} from './ReactTypeOfMode';
|
2018-10-20 02:18:32 +08:00
|
|
|
|
2017-11-03 03:50:03 +08:00
|
|
|
import {
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
enqueueUpdate,
|
Deterministic updates (#10715)
* Deterministic updates
High priority updates typically require less work to render than
low priority ones. It's beneficial to flush those first, in their own
batch, before working on more expensive low priority ones. We do this
even if a high priority is scheduled after a low priority one.
However, we don't want this reordering of updates to affect the terminal
state. State should be deterministic: once all work has been flushed,
the final state should be the same regardless of how they were
scheduled.
To get both properties, we store updates on the queue in insertion
order instead of priority order (always append). Then, when processing
the queue, we skip over updates with insufficient priority. Instead of
removing updates from the queue right after processing them, we only
remove them if there are no unprocessed updates before it in the list.
This means that updates may be processed more than once.
As a bonus, the new implementation is simpler and requires less code.
* Fix ceiling function
Mixed up the operators.
* Remove addUpdate, addReplaceState, et al
These functions don't really do anything. Simpler to use a single
insertUpdateIntoFiber function.
Also splits scheduleUpdate into two functions:
- scheduleWork traverses a fiber's ancestor path and updates their
expiration times.
- scheduleUpdate inserts an update into a fiber's update queue, then
calls scheduleWork.
* Remove getExpirationTime
The last remaining use for getExpirationTime was for top-level async
updates. I moved that check to scheduleUpdate instead.
* Move UpdateQueue insertions back to class module
Moves UpdateQueue related functions out of the scheduler and back into
the class component module. It's a bit awkward that now we need to pass
around createUpdateExpirationForFiber, too. But we can still do without
addUpdate, replaceUpdate, et al.
* Store callbacks as an array of Updates
Simpler this way.
Also moves commitCallbacks back to UpdateQueue module.
* beginUpdateQueue -> processUpdateQueue
* Updates should never have an expiration of NoWork
* Rename expiration related functions
* Fix update queue Flow types
Gets rid of an unneccessary null check
2017-10-14 08:21:25 +08:00
|
|
|
processUpdateQueue,
|
2018-05-15 10:18:47 +08:00
|
|
|
checkHasForceUpdateAfterProcessing,
|
|
|
|
|
resetHasForceUpdateBeforeProcessing,
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
createUpdate,
|
|
|
|
|
ReplaceState,
|
|
|
|
|
ForceUpdate,
|
2019-12-11 08:42:42 +08:00
|
|
|
initializeUpdateQueue,
|
|
|
|
|
cloneUpdateQueue,
|
2020-04-09 10:44:52 +08:00
|
|
|
} from './ReactUpdateQueue.old';
|
2020-04-10 03:51:02 +08:00
|
|
|
import {NoWork} from './ReactFiberExpirationTime.old';
|
2018-05-19 18:29:11 +08:00
|
|
|
import {
|
|
|
|
|
cacheContext,
|
|
|
|
|
getMaskedContext,
|
|
|
|
|
getUnmaskedContext,
|
|
|
|
|
hasContextChanged,
|
Inline fbjs/lib/emptyObject (#13055)
* Inline fbjs/lib/emptyObject
* Explicit naming
* Compare to undefined
* Another approach for detecting whether we can mutate
Each renderer would have its own local LegacyRefsObject function.
While in general we don't want `instanceof`, here it lets us do a simple check: did *we* create the refs object?
Then we can mutate it.
If the check didn't pass, either we're attaching ref for the first time (so we know to use the constructor),
or (unlikely) we're attaching a ref to a component owned by another renderer. In this case, to avoid "losing"
refs, we assign them onto the new object. Even in that case it shouldn't "hop" between renderers anymore.
* Clearer naming
* Add test case for strings refs across renderers
* Use a shared empty object for refs by reading it from React
* Remove string refs from ReactART test
It's not currently possible to resetModules() between several renderers
without also resetting the `React` module. However, that leads to losing
the referential identity of the empty ref object, and thus subsequent
checks in the renderers for whether it is pooled fail (and cause assignments
to a frozen object).
This has always been the case, but we used to work around it by shimming
fbjs/lib/emptyObject in tests and preserving its referential identity.
This won't work anymore because we've inlined it. And preserving referential
identity of React itself wouldn't be great because it could be confusing during
testing (although we might want to revisit this in the future by moving its
stateful parts into a separate package).
For now, I'm removing string ref usage from this test because only this is
the only place in our tests where we hit this problem, and it's only
related to string refs, and not just ref mechanism in general.
* Simplify the condition
2018-06-19 20:41:42 +08:00
|
|
|
emptyContextObject,
|
2020-04-09 10:44:52 +08:00
|
|
|
} from './ReactFiberContext.old';
|
|
|
|
|
import {readContext} from './ReactFiberNewContext.old';
|
2018-05-19 18:29:11 +08:00
|
|
|
import {
|
2019-10-22 04:15:37 +08:00
|
|
|
requestCurrentTimeForUpdate,
|
2018-05-19 18:29:11 +08:00
|
|
|
computeExpirationForFiber,
|
2020-03-12 03:34:39 +08:00
|
|
|
scheduleUpdateOnFiber,
|
2020-04-16 10:10:15 +08:00
|
|
|
priorityLevelToLabel,
|
2020-04-09 10:44:52 +08:00
|
|
|
} from './ReactFiberWorkLoop.old';
|
Split cross-package types from implementation
Some of our internal reconciler types have leaked into other packages.
Usually, these types are treated as opaque; we don't read and write
to its fields. This is good.
However, the type is often passed back to a reconciler method. For
example, React DOM creates a FiberRoot with `createContainer`, then
passes that root to `updateContainer`. It doesn't do anything with the
root except pass it through, but because `updateContainer` expects a
full FiberRoot, React DOM is still coupled to all its fields.
I don't know if there's an idiomatic way to handle this in Flow. Opaque
types are simlar, but those only work within a single file. AFAIK,
there's no way to use a package as the boundary for opaqueness.
The immediate problem this presents is that the reconciler refactor will
involve changes to our internal data structures. I don't want to have to
fork every single package that happens to pass through a Fiber or
FiberRoot, or access any one of its fields. So my current plan is to
share the same Flow type across both forks. The shared type will be a
superset of each implementation's type, e.g. Fiber will have both an
`expirationTime` field and a `lanes` field. The implementations will
diverge, but not the types.
To do this, I lifted the type definitions into a separate module.
2020-04-09 14:48:24 +08:00
|
|
|
import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';
|
2020-04-16 10:10:15 +08:00
|
|
|
import {logForceUpdateScheduled, logStateUpdateScheduled} from './DebugTracing';
|
2016-11-06 02:19:48 +08:00
|
|
|
|
2020-04-09 07:43:51 +08:00
|
|
|
import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
|
|
|
|
|
|
2017-08-14 07:03:31 +08:00
|
|
|
const fakeInternalInstance = {};
|
2016-11-06 02:19:48 +08:00
|
|
|
const isArray = Array.isArray;
|
2016-10-12 01:43:47 +08:00
|
|
|
|
Inline fbjs/lib/emptyObject (#13055)
* Inline fbjs/lib/emptyObject
* Explicit naming
* Compare to undefined
* Another approach for detecting whether we can mutate
Each renderer would have its own local LegacyRefsObject function.
While in general we don't want `instanceof`, here it lets us do a simple check: did *we* create the refs object?
Then we can mutate it.
If the check didn't pass, either we're attaching ref for the first time (so we know to use the constructor),
or (unlikely) we're attaching a ref to a component owned by another renderer. In this case, to avoid "losing"
refs, we assign them onto the new object. Even in that case it shouldn't "hop" between renderers anymore.
* Clearer naming
* Add test case for strings refs across renderers
* Use a shared empty object for refs by reading it from React
* Remove string refs from ReactART test
It's not currently possible to resetModules() between several renderers
without also resetting the `React` module. However, that leads to losing
the referential identity of the empty ref object, and thus subsequent
checks in the renderers for whether it is pooled fail (and cause assignments
to a frozen object).
This has always been the case, but we used to work around it by shimming
fbjs/lib/emptyObject in tests and preserving its referential identity.
This won't work anymore because we've inlined it. And preserving referential
identity of React itself wouldn't be great because it could be confusing during
testing (although we might want to revisit this in the future by moving its
stateful parts into a separate package).
For now, I'm removing string ref usage from this test because only this is
the only place in our tests where we hit this problem, and it's only
related to string refs, and not just ref mechanism in general.
* Simplify the condition
2018-06-19 20:41:42 +08:00
|
|
|
// React.Component uses a shared frozen object by default.
|
|
|
|
|
// We'll use it to determine whether we need to initialize legacy refs.
|
|
|
|
|
export const emptyRefsObject = new React.Component().refs;
|
|
|
|
|
|
2017-12-05 21:47:57 +08:00
|
|
|
let didWarnAboutStateAssignmentForComponent;
|
2018-01-20 01:36:46 +08:00
|
|
|
let didWarnAboutUninitializedState;
|
2018-03-27 04:28:10 +08:00
|
|
|
let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
|
2018-03-23 02:16:54 +08:00
|
|
|
let didWarnAboutLegacyLifecyclesAndDerivedState;
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
let didWarnAboutUndefinedDerivedState;
|
|
|
|
|
let warnOnUndefinedDerivedState;
|
2017-12-05 21:47:57 +08:00
|
|
|
let warnOnInvalidCallback;
|
2018-08-28 21:17:44 +08:00
|
|
|
let didWarnAboutDirectlyAssigningPropsToState;
|
2018-09-26 06:49:46 +08:00
|
|
|
let didWarnAboutContextTypeAndContextTypes;
|
|
|
|
|
let didWarnAboutInvalidateContextType;
|
2017-12-05 21:47:57 +08:00
|
|
|
|
2017-02-10 07:35:59 +08:00
|
|
|
if (__DEV__) {
|
2018-03-27 04:28:10 +08:00
|
|
|
didWarnAboutStateAssignmentForComponent = new Set();
|
|
|
|
|
didWarnAboutUninitializedState = new Set();
|
|
|
|
|
didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set();
|
|
|
|
|
didWarnAboutLegacyLifecyclesAndDerivedState = new Set();
|
2018-08-28 21:17:44 +08:00
|
|
|
didWarnAboutDirectlyAssigningPropsToState = new Set();
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
didWarnAboutUndefinedDerivedState = new Set();
|
2018-09-26 06:49:46 +08:00
|
|
|
didWarnAboutContextTypeAndContextTypes = new Set();
|
|
|
|
|
didWarnAboutInvalidateContextType = new Set();
|
2018-01-20 01:36:46 +08:00
|
|
|
|
2018-03-27 04:28:10 +08:00
|
|
|
const didWarnOnInvalidCallback = new Set();
|
2017-10-26 02:07:54 +08:00
|
|
|
|
2017-12-05 21:47:57 +08:00
|
|
|
warnOnInvalidCallback = function(callback: mixed, callerName: string) {
|
2018-01-07 19:52:52 +08:00
|
|
|
if (callback === null || typeof callback === 'function') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const key = `${callerName}_${(callback: any)}`;
|
2018-03-27 04:28:10 +08:00
|
|
|
if (!didWarnOnInvalidCallback.has(key)) {
|
|
|
|
|
didWarnOnInvalidCallback.add(key);
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-01-07 19:52:52 +08:00
|
|
|
'%s(...): Expected the last optional `callback` argument to be a ' +
|
|
|
|
|
'function. Instead received: %s.',
|
|
|
|
|
callerName,
|
|
|
|
|
callback,
|
|
|
|
|
);
|
|
|
|
|
}
|
2017-02-10 07:35:59 +08:00
|
|
|
};
|
2017-08-14 07:03:31 +08:00
|
|
|
|
2018-07-12 22:32:06 +08:00
|
|
|
warnOnUndefinedDerivedState = function(type, partialState) {
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
if (partialState === undefined) {
|
2018-07-12 22:32:06 +08:00
|
|
|
const componentName = getComponentName(type) || 'Component';
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
|
|
|
|
|
didWarnAboutUndefinedDerivedState.add(componentName);
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
|
|
|
|
|
'You have returned undefined.',
|
|
|
|
|
componentName,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-08-14 07:03:31 +08:00
|
|
|
// This is so gross but it's at least non-critical and can be removed if
|
|
|
|
|
// it causes problems. This is meant to give a nicer error message for
|
|
|
|
|
// ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component,
|
|
|
|
|
// ...)) which otherwise throws a "_processChildContext is not a function"
|
|
|
|
|
// exception.
|
|
|
|
|
Object.defineProperty(fakeInternalInstance, '_processChildContext', {
|
|
|
|
|
enumerable: false,
|
|
|
|
|
value: function() {
|
|
|
|
|
invariant(
|
|
|
|
|
false,
|
|
|
|
|
'_processChildContext is not available in React 16+. This likely ' +
|
|
|
|
|
'means you have multiple copies of React and are attempting to nest ' +
|
|
|
|
|
'a React 15 tree inside a React 16 tree using ' +
|
|
|
|
|
"unstable_renderSubtreeIntoContainer, which isn't supported. Try " +
|
|
|
|
|
'to make sure you have only one copy of React (and ideally, switch ' +
|
2017-09-12 05:23:54 +08:00
|
|
|
'to ReactDOM.createPortal).',
|
2017-08-14 07:03:31 +08:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
Object.freeze(fakeInternalInstance);
|
2017-02-10 07:35:59 +08:00
|
|
|
}
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
|
|
|
|
|
export function applyDerivedStateFromProps(
|
|
|
|
|
workInProgress: Fiber,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor: any,
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
getDerivedStateFromProps: (props: any, state: any) => any,
|
|
|
|
|
nextProps: any,
|
|
|
|
|
) {
|
|
|
|
|
const prevState = workInProgress.memoizedState;
|
|
|
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
if (
|
2019-11-05 06:07:05 +08:00
|
|
|
debugRenderPhaseSideEffectsForStrictMode &&
|
|
|
|
|
workInProgress.mode & StrictMode
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
) {
|
2020-04-09 07:43:51 +08:00
|
|
|
disableLogs();
|
|
|
|
|
try {
|
|
|
|
|
// Invoke the function an extra time to help detect side-effects.
|
|
|
|
|
getDerivedStateFromProps(nextProps, prevState);
|
|
|
|
|
} finally {
|
|
|
|
|
reenableLogs();
|
|
|
|
|
}
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
}
|
|
|
|
|
}
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
|
|
|
|
|
const partialState = getDerivedStateFromProps(nextProps, prevState);
|
|
|
|
|
|
|
|
|
|
if (__DEV__) {
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
warnOnUndefinedDerivedState(ctor, partialState);
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
}
|
|
|
|
|
// Merge the partial state and the previous state.
|
|
|
|
|
const memoizedState =
|
|
|
|
|
partialState === null || partialState === undefined
|
|
|
|
|
? prevState
|
|
|
|
|
: Object.assign({}, prevState, partialState);
|
|
|
|
|
workInProgress.memoizedState = memoizedState;
|
|
|
|
|
|
|
|
|
|
// Once the update queue is empty, persist the derived state onto the
|
|
|
|
|
// base state.
|
2019-12-11 08:42:42 +08:00
|
|
|
if (workInProgress.expirationTime === NoWork) {
|
|
|
|
|
// Queue is always non-null for classes
|
|
|
|
|
const updateQueue: UpdateQueue<any> = (workInProgress.updateQueue: any);
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
updateQueue.baseState = memoizedState;
|
|
|
|
|
}
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
}
|
2017-02-10 07:35:59 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
const classComponentUpdater = {
|
|
|
|
|
isMounted,
|
|
|
|
|
enqueueSetState(inst, payload, callback) {
|
2018-11-20 07:32:54 +08:00
|
|
|
const fiber = getInstance(inst);
|
2019-10-22 04:15:37 +08:00
|
|
|
const currentTime = requestCurrentTimeForUpdate();
|
2019-05-17 07:51:18 +08:00
|
|
|
const suspenseConfig = requestCurrentSuspenseConfig();
|
|
|
|
|
const expirationTime = computeExpirationForFiber(
|
|
|
|
|
currentTime,
|
|
|
|
|
fiber,
|
|
|
|
|
suspenseConfig,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const update = createUpdate(expirationTime, suspenseConfig);
|
2018-05-19 18:29:11 +08:00
|
|
|
update.payload = payload;
|
|
|
|
|
if (callback !== undefined && callback !== null) {
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
warnOnInvalidCallback(callback, 'setState');
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
update.callback = callback;
|
|
|
|
|
}
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
|
[Experimental] API for reading context from within any render phase function (#13139)
* Store list of contexts on the fiber
Currently, context can only be read by a special type of component,
ContextConsumer. We want to add support to all fibers, including
classes and functional components.
Each fiber may read from one or more contexts. To enable quick, mono-
morphic access of this list, we'll store them on a fiber property.
* Context.unstable_read
unstable_read can be called anywhere within the render phase. That
includes the render method, getDerivedStateFromProps, constructors,
functional components, and context consumer render props.
If it's called outside the render phase, an error is thrown.
* Remove vestigial context cursor
Wasn't being used.
* Split fiber.expirationTime into two separate fields
Currently, the `expirationTime` field represents the pending work of
both the fiber itself — including new props, state, and context — and of
any updates in that fiber's subtree.
This commit adds a second field called `childExpirationTime`. Now
`expirationTime` only represents the pending work of the fiber itself.
The subtree's pending work is represented by `childExpirationTime`.
The biggest advantage is it requires fewer checks to bailout on already
finished work. For most types of work, if the `expirationTime` does not
match the render expiration time, we can bailout immediately without
any further checks. This won't work for fibers that have
`shouldComponentUpdate` semantics (class components), for which we still
need to check for props and state changes explicitly.
* Performance nits
Optimize `readContext` for most common case
2018-07-21 07:49:06 +08:00
|
|
|
enqueueUpdate(fiber, update);
|
2020-03-12 03:34:39 +08:00
|
|
|
scheduleUpdateOnFiber(fiber, expirationTime);
|
2020-04-16 10:10:15 +08:00
|
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
if (enableDebugTracing) {
|
|
|
|
|
if (fiber.mode & DebugTracingMode) {
|
|
|
|
|
const label = priorityLevelToLabel(
|
|
|
|
|
((update.priority: any): ReactPriorityLevel),
|
|
|
|
|
);
|
|
|
|
|
const name = getComponentName(fiber.type) || 'Unknown';
|
|
|
|
|
logStateUpdateScheduled(name, label, payload);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
},
|
|
|
|
|
enqueueReplaceState(inst, payload, callback) {
|
2018-11-20 07:32:54 +08:00
|
|
|
const fiber = getInstance(inst);
|
2019-10-22 04:15:37 +08:00
|
|
|
const currentTime = requestCurrentTimeForUpdate();
|
2019-05-17 07:51:18 +08:00
|
|
|
const suspenseConfig = requestCurrentSuspenseConfig();
|
|
|
|
|
const expirationTime = computeExpirationForFiber(
|
|
|
|
|
currentTime,
|
|
|
|
|
fiber,
|
|
|
|
|
suspenseConfig,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const update = createUpdate(expirationTime, suspenseConfig);
|
2018-05-19 18:29:11 +08:00
|
|
|
update.tag = ReplaceState;
|
|
|
|
|
update.payload = payload;
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (callback !== undefined && callback !== null) {
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
warnOnInvalidCallback(callback, 'replaceState');
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
update.callback = callback;
|
|
|
|
|
}
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
|
[Experimental] API for reading context from within any render phase function (#13139)
* Store list of contexts on the fiber
Currently, context can only be read by a special type of component,
ContextConsumer. We want to add support to all fibers, including
classes and functional components.
Each fiber may read from one or more contexts. To enable quick, mono-
morphic access of this list, we'll store them on a fiber property.
* Context.unstable_read
unstable_read can be called anywhere within the render phase. That
includes the render method, getDerivedStateFromProps, constructors,
functional components, and context consumer render props.
If it's called outside the render phase, an error is thrown.
* Remove vestigial context cursor
Wasn't being used.
* Split fiber.expirationTime into two separate fields
Currently, the `expirationTime` field represents the pending work of
both the fiber itself — including new props, state, and context — and of
any updates in that fiber's subtree.
This commit adds a second field called `childExpirationTime`. Now
`expirationTime` only represents the pending work of the fiber itself.
The subtree's pending work is represented by `childExpirationTime`.
The biggest advantage is it requires fewer checks to bailout on already
finished work. For most types of work, if the `expirationTime` does not
match the render expiration time, we can bailout immediately without
any further checks. This won't work for fibers that have
`shouldComponentUpdate` semantics (class components), for which we still
need to check for props and state changes explicitly.
* Performance nits
Optimize `readContext` for most common case
2018-07-21 07:49:06 +08:00
|
|
|
enqueueUpdate(fiber, update);
|
2020-03-12 03:34:39 +08:00
|
|
|
scheduleUpdateOnFiber(fiber, expirationTime);
|
2020-04-16 10:10:15 +08:00
|
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
if (enableDebugTracing) {
|
|
|
|
|
if (fiber.mode & DebugTracingMode) {
|
|
|
|
|
const label = priorityLevelToLabel(
|
|
|
|
|
((update.priority: any): ReactPriorityLevel),
|
|
|
|
|
);
|
|
|
|
|
const name = getComponentName(fiber.type) || 'Unknown';
|
|
|
|
|
logStateUpdateScheduled(name, label, payload);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
},
|
|
|
|
|
enqueueForceUpdate(inst, callback) {
|
2018-11-20 07:32:54 +08:00
|
|
|
const fiber = getInstance(inst);
|
2019-10-22 04:15:37 +08:00
|
|
|
const currentTime = requestCurrentTimeForUpdate();
|
2019-05-17 07:51:18 +08:00
|
|
|
const suspenseConfig = requestCurrentSuspenseConfig();
|
|
|
|
|
const expirationTime = computeExpirationForFiber(
|
|
|
|
|
currentTime,
|
|
|
|
|
fiber,
|
|
|
|
|
suspenseConfig,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const update = createUpdate(expirationTime, suspenseConfig);
|
2018-05-19 18:29:11 +08:00
|
|
|
update.tag = ForceUpdate;
|
2016-11-10 00:30:41 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (callback !== undefined && callback !== null) {
|
2016-11-10 00:30:41 +08:00
|
|
|
if (__DEV__) {
|
2018-05-19 18:29:11 +08:00
|
|
|
warnOnInvalidCallback(callback, 'forceUpdate');
|
2016-11-10 00:30:41 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
update.callback = callback;
|
2016-10-27 07:56:46 +08:00
|
|
|
}
|
|
|
|
|
|
[Experimental] API for reading context from within any render phase function (#13139)
* Store list of contexts on the fiber
Currently, context can only be read by a special type of component,
ContextConsumer. We want to add support to all fibers, including
classes and functional components.
Each fiber may read from one or more contexts. To enable quick, mono-
morphic access of this list, we'll store them on a fiber property.
* Context.unstable_read
unstable_read can be called anywhere within the render phase. That
includes the render method, getDerivedStateFromProps, constructors,
functional components, and context consumer render props.
If it's called outside the render phase, an error is thrown.
* Remove vestigial context cursor
Wasn't being used.
* Split fiber.expirationTime into two separate fields
Currently, the `expirationTime` field represents the pending work of
both the fiber itself — including new props, state, and context — and of
any updates in that fiber's subtree.
This commit adds a second field called `childExpirationTime`. Now
`expirationTime` only represents the pending work of the fiber itself.
The subtree's pending work is represented by `childExpirationTime`.
The biggest advantage is it requires fewer checks to bailout on already
finished work. For most types of work, if the `expirationTime` does not
match the render expiration time, we can bailout immediately without
any further checks. This won't work for fibers that have
`shouldComponentUpdate` semantics (class components), for which we still
need to check for props and state changes explicitly.
* Performance nits
Optimize `readContext` for most common case
2018-07-21 07:49:06 +08:00
|
|
|
enqueueUpdate(fiber, update);
|
2020-03-12 03:34:39 +08:00
|
|
|
scheduleUpdateOnFiber(fiber, expirationTime);
|
2020-04-16 10:10:15 +08:00
|
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
if (enableDebugTracing) {
|
|
|
|
|
if (fiber.mode & DebugTracingMode) {
|
|
|
|
|
const label = priorityLevelToLabel(
|
|
|
|
|
((update.priority: any): ReactPriorityLevel),
|
|
|
|
|
);
|
|
|
|
|
const name = getComponentName(fiber.type) || 'Unknown';
|
|
|
|
|
logForceUpdateScheduled(name, label);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function checkShouldComponentUpdate(
|
|
|
|
|
workInProgress,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor,
|
2018-05-19 18:29:11 +08:00
|
|
|
oldProps,
|
|
|
|
|
newProps,
|
|
|
|
|
oldState,
|
|
|
|
|
newState,
|
2018-09-26 06:49:46 +08:00
|
|
|
nextContext,
|
2018-05-19 18:29:11 +08:00
|
|
|
) {
|
|
|
|
|
const instance = workInProgress.stateNode;
|
|
|
|
|
if (typeof instance.shouldComponentUpdate === 'function') {
|
2020-01-31 05:03:44 +08:00
|
|
|
if (__DEV__) {
|
|
|
|
|
if (
|
|
|
|
|
debugRenderPhaseSideEffectsForStrictMode &&
|
|
|
|
|
workInProgress.mode & StrictMode
|
|
|
|
|
) {
|
2020-04-09 07:43:51 +08:00
|
|
|
disableLogs();
|
|
|
|
|
try {
|
|
|
|
|
// Invoke the function an extra time to help detect side-effects.
|
|
|
|
|
instance.shouldComponentUpdate(newProps, newState, nextContext);
|
|
|
|
|
} finally {
|
|
|
|
|
reenableLogs();
|
|
|
|
|
}
|
2020-01-31 05:03:44 +08:00
|
|
|
}
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
const shouldUpdate = instance.shouldComponentUpdate(
|
|
|
|
|
newProps,
|
|
|
|
|
newState,
|
2018-09-26 06:49:46 +08:00
|
|
|
nextContext,
|
2018-05-19 18:29:11 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (__DEV__) {
|
2019-12-11 11:28:14 +08:00
|
|
|
if (shouldUpdate === undefined) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
|
|
|
|
|
'boolean value. Make sure to return true or false.',
|
|
|
|
|
getComponentName(ctor) || 'Component',
|
|
|
|
|
);
|
|
|
|
|
}
|
2016-10-27 07:56:46 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
return shouldUpdate;
|
2016-10-27 07:56:46 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
|
|
|
|
|
return (
|
|
|
|
|
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
|
|
|
|
|
);
|
|
|
|
|
}
|
2017-11-02 05:01:24 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
2017-11-02 05:01:24 +08:00
|
|
|
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
|
2018-05-19 18:29:11 +08:00
|
|
|
const instance = workInProgress.stateNode;
|
|
|
|
|
if (__DEV__) {
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
const name = getComponentName(ctor) || 'Component';
|
2018-05-19 18:29:11 +08:00
|
|
|
const renderPresent = instance.render;
|
|
|
|
|
|
|
|
|
|
if (!renderPresent) {
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
if (ctor.prototype && typeof ctor.prototype.render === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-05-19 18:29:11 +08:00
|
|
|
'%s(...): No `render` method found on the returned component ' +
|
|
|
|
|
'instance: did you accidentally return an object from the constructor?',
|
|
|
|
|
name,
|
2017-04-21 22:55:16 +08:00
|
|
|
);
|
2018-05-19 18:29:11 +08:00
|
|
|
} else {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-05-19 18:29:11 +08:00
|
|
|
'%s(...): No `render` method found on the returned component ' +
|
|
|
|
|
'instance: you may have forgotten to define `render`.',
|
|
|
|
|
name,
|
2018-03-27 04:28:10 +08:00
|
|
|
);
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
2018-03-27 04:28:10 +08:00
|
|
|
|
2019-12-11 11:28:14 +08:00
|
|
|
if (
|
|
|
|
|
instance.getInitialState &&
|
|
|
|
|
!instance.getInitialState.isReactClassApproved &&
|
|
|
|
|
!instance.state
|
|
|
|
|
) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'getInitialState was defined on %s, a plain JavaScript class. ' +
|
|
|
|
|
'This is only supported for classes created using React.createClass. ' +
|
|
|
|
|
'Did you mean to define a state property instead?',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
instance.getDefaultProps &&
|
|
|
|
|
!instance.getDefaultProps.isReactClassApproved
|
|
|
|
|
) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'getDefaultProps was defined on %s, a plain JavaScript class. ' +
|
|
|
|
|
'This is only supported for classes created using React.createClass. ' +
|
|
|
|
|
'Use a static property to define defaultProps instead.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (instance.propTypes) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'propTypes was defined as an instance property on %s. Use a static ' +
|
|
|
|
|
'property to define propTypes instead.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (instance.contextType) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'contextType was defined as an instance property on %s. Use a static ' +
|
|
|
|
|
'property to define contextType instead.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-09-26 06:49:46 +08:00
|
|
|
|
2019-08-02 08:21:32 +08:00
|
|
|
if (disableLegacyContext) {
|
|
|
|
|
if (ctor.childContextTypes) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-08-02 08:21:32 +08:00
|
|
|
'%s uses the legacy childContextTypes API which is no longer supported. ' +
|
|
|
|
|
'Use React.createContext() instead.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (ctor.contextTypes) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-08-02 08:21:32 +08:00
|
|
|
'%s uses the legacy contextTypes API which is no longer supported. ' +
|
|
|
|
|
'Use React.createContext() with static contextType instead.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2019-12-11 11:28:14 +08:00
|
|
|
if (instance.contextTypes) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'contextTypes was defined as an instance property on %s. Use a static ' +
|
|
|
|
|
'property to define contextTypes instead.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-08-02 08:21:32 +08:00
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
ctor.contextType &&
|
|
|
|
|
ctor.contextTypes &&
|
|
|
|
|
!didWarnAboutContextTypeAndContextTypes.has(ctor)
|
|
|
|
|
) {
|
|
|
|
|
didWarnAboutContextTypeAndContextTypes.add(ctor);
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-08-02 08:21:32 +08:00
|
|
|
'%s declares both contextTypes and contextType static properties. ' +
|
|
|
|
|
'The legacy contextTypes property will be ignored.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-09-26 06:49:46 +08:00
|
|
|
}
|
|
|
|
|
|
2019-12-11 11:28:14 +08:00
|
|
|
if (typeof instance.componentShouldUpdate === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s has a method called ' +
|
|
|
|
|
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
|
|
|
|
|
'The name is phrased as a question because the function is ' +
|
|
|
|
|
'expected to return a value.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
if (
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor.prototype &&
|
|
|
|
|
ctor.prototype.isPureReactComponent &&
|
2018-05-19 18:29:11 +08:00
|
|
|
typeof instance.shouldComponentUpdate !== 'undefined'
|
|
|
|
|
) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-05-19 18:29:11 +08:00
|
|
|
'%s has a method called shouldComponentUpdate(). ' +
|
|
|
|
|
'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
|
|
|
|
|
'Please extend React.Component if shouldComponentUpdate is used.',
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
getComponentName(ctor) || 'A pure component',
|
2018-03-23 01:54:51 +08:00
|
|
|
);
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
2019-12-11 11:28:14 +08:00
|
|
|
if (typeof instance.componentDidUnmount === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s has a method called ' +
|
|
|
|
|
'componentDidUnmount(). But there is no such lifecycle method. ' +
|
|
|
|
|
'Did you mean componentWillUnmount()?',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (typeof instance.componentDidReceiveProps === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s has a method called ' +
|
|
|
|
|
'componentDidReceiveProps(). But there is no such lifecycle method. ' +
|
|
|
|
|
'If you meant to update the state in response to changing props, ' +
|
|
|
|
|
'use componentWillReceiveProps(). If you meant to fetch data or ' +
|
|
|
|
|
'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (typeof instance.componentWillRecieveProps === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s has a method called ' +
|
|
|
|
|
'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (typeof instance.UNSAFE_componentWillRecieveProps === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s has a method called ' +
|
|
|
|
|
'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
const hasMutatedProps = instance.props !== newProps;
|
2019-12-11 11:28:14 +08:00
|
|
|
if (instance.props !== undefined && hasMutatedProps) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s(...): When calling super() in `%s`, make sure to pass ' +
|
|
|
|
|
"up the same props that your component's constructor was passed.",
|
|
|
|
|
name,
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (instance.defaultProps) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'Setting defaultProps as an instance property on %s is not supported and will be ignored.' +
|
|
|
|
|
' Instead, define defaultProps as a static property on %s.',
|
|
|
|
|
name,
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
typeof instance.getSnapshotBeforeUpdate === 'function' &&
|
|
|
|
|
typeof instance.componentDidUpdate !== 'function' &&
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
!didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)
|
2018-05-19 18:29:11 +08:00
|
|
|
) {
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor);
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-05-19 18:29:11 +08:00
|
|
|
'%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' +
|
|
|
|
|
'This component defines getSnapshotBeforeUpdate() only.',
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
getComponentName(ctor),
|
2018-03-23 01:54:51 +08:00
|
|
|
);
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
|
|
|
|
|
2019-12-11 11:28:14 +08:00
|
|
|
if (typeof instance.getDerivedStateFromProps === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s: getDerivedStateFromProps() is defined as an instance method ' +
|
|
|
|
|
'and will be ignored. Instead, declare it as a static method.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (typeof instance.getDerivedStateFromError === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s: getDerivedStateFromError() is defined as an instance method ' +
|
|
|
|
|
'and will be ignored. Instead, declare it as a static method.',
|
|
|
|
|
name,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (typeof ctor.getSnapshotBeforeUpdate === 'function') {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2019-12-11 11:28:14 +08:00
|
|
|
'%s: getSnapshotBeforeUpdate() is defined as a static method ' +
|
|
|
|
|
'and will be ignored. Instead, declare it as an instance method.',
|
2018-07-17 05:31:59 +08:00
|
|
|
name,
|
|
|
|
|
);
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
2019-12-11 11:28:14 +08:00
|
|
|
const state = instance.state;
|
|
|
|
|
if (state && (typeof state !== 'object' || isArray(state))) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error('%s.state: must be set to an object or null', name);
|
2019-12-11 11:28:14 +08:00
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
typeof instance.getChildContext === 'function' &&
|
|
|
|
|
typeof ctor.childContextTypes !== 'object'
|
|
|
|
|
) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-05-19 18:29:11 +08:00
|
|
|
'%s.getChildContext(): childContextTypes must be defined in order to ' +
|
|
|
|
|
'use getChildContext().',
|
2018-03-29 04:35:32 +08:00
|
|
|
name,
|
|
|
|
|
);
|
2016-11-06 02:19:48 +08:00
|
|
|
}
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
2016-11-06 02:19:48 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
|
|
|
|
|
instance.updater = classComponentUpdater;
|
|
|
|
|
workInProgress.stateNode = instance;
|
|
|
|
|
// The instance needs access to the fiber so that it can schedule updates
|
2018-11-20 07:32:54 +08:00
|
|
|
setInstance(instance, workInProgress);
|
2018-05-19 18:29:11 +08:00
|
|
|
if (__DEV__) {
|
|
|
|
|
instance._reactInternalInstance = fakeInternalInstance;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function constructClassInstance(
|
|
|
|
|
workInProgress: Fiber,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor: any,
|
2018-05-19 18:29:11 +08:00
|
|
|
props: any,
|
|
|
|
|
): any {
|
2018-09-26 06:49:46 +08:00
|
|
|
let isLegacyContextConsumer = false;
|
|
|
|
|
let unmaskedContext = emptyContextObject;
|
2019-08-02 08:21:32 +08:00
|
|
|
let context = emptyContextObject;
|
2018-09-26 06:49:46 +08:00
|
|
|
const contextType = ctor.contextType;
|
2019-03-19 21:31:26 +08:00
|
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
if ('contextType' in ctor) {
|
2020-04-02 03:35:52 +08:00
|
|
|
const isValid =
|
2019-03-19 21:31:26 +08:00
|
|
|
// Allow null for conditional declaration
|
|
|
|
|
contextType === null ||
|
|
|
|
|
(contextType !== undefined &&
|
|
|
|
|
contextType.$$typeof === REACT_CONTEXT_TYPE &&
|
|
|
|
|
contextType._context === undefined); // Not a <Context.Consumer>
|
|
|
|
|
|
|
|
|
|
if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
|
2018-09-29 04:12:26 +08:00
|
|
|
didWarnAboutInvalidateContextType.add(ctor);
|
2019-03-19 21:31:26 +08:00
|
|
|
|
|
|
|
|
let addendum = '';
|
|
|
|
|
if (contextType === undefined) {
|
|
|
|
|
addendum =
|
|
|
|
|
' However, it is set to undefined. ' +
|
|
|
|
|
'This can be caused by a typo or by mixing up named and default imports. ' +
|
|
|
|
|
'This can also happen due to a circular dependency, so ' +
|
|
|
|
|
'try moving the createContext() call to a separate file.';
|
|
|
|
|
} else if (typeof contextType !== 'object') {
|
|
|
|
|
addendum = ' However, it is set to a ' + typeof contextType + '.';
|
|
|
|
|
} else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
|
|
|
|
|
addendum = ' Did you accidentally pass the Context.Provider instead?';
|
|
|
|
|
} else if (contextType._context !== undefined) {
|
|
|
|
|
// <Context.Consumer>
|
|
|
|
|
addendum = ' Did you accidentally pass the Context.Consumer instead?';
|
|
|
|
|
} else {
|
|
|
|
|
addendum =
|
|
|
|
|
' However, it is set to an object with keys {' +
|
|
|
|
|
Object.keys(contextType).join(', ') +
|
|
|
|
|
'}.';
|
|
|
|
|
}
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-09-29 04:12:26 +08:00
|
|
|
'%s defines an invalid contextType. ' +
|
2019-03-19 21:31:26 +08:00
|
|
|
'contextType should point to the Context object returned by React.createContext().%s',
|
2018-09-29 04:12:26 +08:00
|
|
|
getComponentName(ctor) || 'Component',
|
2019-03-19 21:31:26 +08:00
|
|
|
addendum,
|
2018-09-29 04:12:26 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-19 21:31:26 +08:00
|
|
|
}
|
2018-09-29 04:12:26 +08:00
|
|
|
|
2019-03-19 21:31:26 +08:00
|
|
|
if (typeof contextType === 'object' && contextType !== null) {
|
2018-10-17 02:58:00 +08:00
|
|
|
context = readContext((contextType: any));
|
2019-08-02 08:21:32 +08:00
|
|
|
} else if (!disableLegacyContext) {
|
2018-09-26 06:49:46 +08:00
|
|
|
unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
|
|
|
|
const contextTypes = ctor.contextTypes;
|
|
|
|
|
isLegacyContextConsumer =
|
|
|
|
|
contextTypes !== null && contextTypes !== undefined;
|
|
|
|
|
context = isLegacyContextConsumer
|
|
|
|
|
? getMaskedContext(workInProgress, unmaskedContext)
|
|
|
|
|
: emptyContextObject;
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
// Instantiate twice to help detect side-effects.
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
if (
|
2019-11-05 06:07:05 +08:00
|
|
|
debugRenderPhaseSideEffectsForStrictMode &&
|
|
|
|
|
workInProgress.mode & StrictMode
|
2018-05-19 18:29:11 +08:00
|
|
|
) {
|
2020-04-09 07:43:51 +08:00
|
|
|
disableLogs();
|
|
|
|
|
try {
|
|
|
|
|
new ctor(props, context); // eslint-disable-line no-new
|
|
|
|
|
} finally {
|
|
|
|
|
reenableLogs();
|
|
|
|
|
}
|
2017-08-14 07:03:31 +08:00
|
|
|
}
|
2016-10-19 17:06:36 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
const instance = new ctor(props, context);
|
|
|
|
|
const state = (workInProgress.memoizedState =
|
|
|
|
|
instance.state !== null && instance.state !== undefined
|
|
|
|
|
? instance.state
|
|
|
|
|
: null);
|
|
|
|
|
adoptClassInstance(workInProgress, instance);
|
|
|
|
|
|
|
|
|
|
if (__DEV__) {
|
|
|
|
|
if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {
|
2018-07-12 22:32:06 +08:00
|
|
|
const componentName = getComponentName(ctor) || 'Component';
|
2018-05-19 18:29:11 +08:00
|
|
|
if (!didWarnAboutUninitializedState.has(componentName)) {
|
|
|
|
|
didWarnAboutUninitializedState.add(componentName);
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-08-03 23:09:57 +08:00
|
|
|
'`%s` uses `getDerivedStateFromProps` but its initial state is ' +
|
|
|
|
|
'%s. This is not recommended. Instead, define the initial state by ' +
|
|
|
|
|
'assigning an object to `this.state` in the constructor of `%s`. ' +
|
|
|
|
|
'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.',
|
2018-05-19 18:29:11 +08:00
|
|
|
componentName,
|
|
|
|
|
instance.state === null ? 'null' : 'undefined',
|
2018-08-03 23:09:57 +08:00
|
|
|
componentName,
|
2018-05-19 18:29:11 +08:00
|
|
|
);
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
}
|
2018-01-25 07:06:25 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
// If new component APIs are defined, "unsafe" lifecycles won't be called.
|
|
|
|
|
// Warn about these lifecycles if they are present.
|
|
|
|
|
// Don't warn about react-lifecycles-compat polyfilled methods though.
|
|
|
|
|
if (
|
|
|
|
|
typeof ctor.getDerivedStateFromProps === 'function' ||
|
|
|
|
|
typeof instance.getSnapshotBeforeUpdate === 'function'
|
|
|
|
|
) {
|
|
|
|
|
let foundWillMountName = null;
|
|
|
|
|
let foundWillReceivePropsName = null;
|
|
|
|
|
let foundWillUpdateName = null;
|
|
|
|
|
if (
|
|
|
|
|
typeof instance.componentWillMount === 'function' &&
|
|
|
|
|
instance.componentWillMount.__suppressDeprecationWarning !== true
|
|
|
|
|
) {
|
|
|
|
|
foundWillMountName = 'componentWillMount';
|
|
|
|
|
} else if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
|
|
|
|
foundWillMountName = 'UNSAFE_componentWillMount';
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
typeof instance.componentWillReceiveProps === 'function' &&
|
|
|
|
|
instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
|
|
|
|
|
) {
|
|
|
|
|
foundWillReceivePropsName = 'componentWillReceiveProps';
|
|
|
|
|
} else if (
|
|
|
|
|
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
|
|
|
|
|
) {
|
|
|
|
|
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
|
|
|
|
|
}
|
2018-03-27 04:28:10 +08:00
|
|
|
if (
|
2018-05-19 18:29:11 +08:00
|
|
|
typeof instance.componentWillUpdate === 'function' &&
|
|
|
|
|
instance.componentWillUpdate.__suppressDeprecationWarning !== true
|
|
|
|
|
) {
|
|
|
|
|
foundWillUpdateName = 'componentWillUpdate';
|
|
|
|
|
} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
|
|
|
|
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
foundWillMountName !== null ||
|
|
|
|
|
foundWillReceivePropsName !== null ||
|
|
|
|
|
foundWillUpdateName !== null
|
2018-03-27 04:28:10 +08:00
|
|
|
) {
|
2018-07-12 22:32:06 +08:00
|
|
|
const componentName = getComponentName(ctor) || 'Component';
|
2018-05-19 18:29:11 +08:00
|
|
|
const newApiName =
|
|
|
|
|
typeof ctor.getDerivedStateFromProps === 'function'
|
|
|
|
|
? 'getDerivedStateFromProps()'
|
|
|
|
|
: 'getSnapshotBeforeUpdate()';
|
|
|
|
|
if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(componentName)) {
|
|
|
|
|
didWarnAboutLegacyLifecyclesAndDerivedState.add(componentName);
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-05-19 18:29:11 +08:00
|
|
|
'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
|
|
|
|
|
'%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +
|
|
|
|
|
'The above lifecycles should be removed. Learn more about this warning here:\n' +
|
2019-08-09 19:18:39 +08:00
|
|
|
'https://fb.me/react-unsafe-component-lifecycles',
|
2018-03-27 04:28:10 +08:00
|
|
|
componentName,
|
2018-05-19 18:29:11 +08:00
|
|
|
newApiName,
|
|
|
|
|
foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
|
|
|
|
|
foundWillReceivePropsName !== null
|
|
|
|
|
? `\n ${foundWillReceivePropsName}`
|
|
|
|
|
: '',
|
|
|
|
|
foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
|
2018-03-27 04:28:10 +08:00
|
|
|
);
|
2018-03-23 02:16:54 +08:00
|
|
|
}
|
2018-03-27 04:28:10 +08:00
|
|
|
}
|
2018-01-20 01:36:46 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
2018-01-20 01:36:46 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
// Cache unmasked context so we can avoid recreating masked context unless necessary.
|
|
|
|
|
// ReactFiberContext usually updates this cache but can't for newly-created instances.
|
2018-09-26 06:49:46 +08:00
|
|
|
if (isLegacyContextConsumer) {
|
2018-05-19 18:29:11 +08:00
|
|
|
cacheContext(workInProgress, unmaskedContext, context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
2017-01-08 00:53:24 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
function callComponentWillMount(workInProgress, instance) {
|
|
|
|
|
const oldState = instance.state;
|
|
|
|
|
|
|
|
|
|
if (typeof instance.componentWillMount === 'function') {
|
|
|
|
|
instance.componentWillMount();
|
|
|
|
|
}
|
|
|
|
|
if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
|
|
|
|
instance.UNSAFE_componentWillMount();
|
2016-10-19 17:06:36 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (oldState !== instance.state) {
|
|
|
|
|
if (__DEV__) {
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-05-19 18:29:11 +08:00
|
|
|
'%s.componentWillMount(): Assigning directly to this.state is ' +
|
|
|
|
|
"deprecated (except inside a component's " +
|
|
|
|
|
'constructor). Use setState instead.',
|
2018-07-12 22:32:06 +08:00
|
|
|
getComponentName(workInProgress.type) || 'Component',
|
2018-05-19 18:29:11 +08:00
|
|
|
);
|
2018-01-20 01:36:46 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-20 01:36:46 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
function callComponentWillReceiveProps(
|
|
|
|
|
workInProgress,
|
|
|
|
|
instance,
|
|
|
|
|
newProps,
|
2018-09-26 06:49:46 +08:00
|
|
|
nextContext,
|
2018-05-19 18:29:11 +08:00
|
|
|
) {
|
|
|
|
|
const oldState = instance.state;
|
|
|
|
|
if (typeof instance.componentWillReceiveProps === 'function') {
|
2018-09-26 06:49:46 +08:00
|
|
|
instance.componentWillReceiveProps(newProps, nextContext);
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
|
|
|
|
if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
|
2018-09-26 06:49:46 +08:00
|
|
|
instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
2017-05-06 05:00:59 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (instance.state !== oldState) {
|
|
|
|
|
if (__DEV__) {
|
2018-07-12 22:32:06 +08:00
|
|
|
const componentName =
|
|
|
|
|
getComponentName(workInProgress.type) || 'Component';
|
2018-05-19 18:29:11 +08:00
|
|
|
if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
|
|
|
|
|
didWarnAboutStateAssignmentForComponent.add(componentName);
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-05-19 18:29:11 +08:00
|
|
|
'%s.componentWillReceiveProps(): Assigning directly to ' +
|
|
|
|
|
"this.state is deprecated (except inside a component's " +
|
2017-05-06 05:00:59 +08:00
|
|
|
'constructor). Use setState instead.',
|
2018-05-19 18:29:11 +08:00
|
|
|
componentName,
|
2017-05-06 05:00:59 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
|
2017-05-06 05:00:59 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
2017-05-06 05:00:59 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
// Invokes the mount life-cycles on a previously never rendered instance.
|
|
|
|
|
function mountClassInstance(
|
|
|
|
|
workInProgress: Fiber,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor: any,
|
|
|
|
|
newProps: any,
|
2018-05-19 18:29:11 +08:00
|
|
|
renderExpirationTime: ExpirationTime,
|
|
|
|
|
): void {
|
|
|
|
|
if (__DEV__) {
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
checkClassInstance(workInProgress, ctor, newProps);
|
2018-01-20 01:36:46 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
const instance = workInProgress.stateNode;
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
instance.props = newProps;
|
2018-05-19 18:29:11 +08:00
|
|
|
instance.state = workInProgress.memoizedState;
|
Inline fbjs/lib/emptyObject (#13055)
* Inline fbjs/lib/emptyObject
* Explicit naming
* Compare to undefined
* Another approach for detecting whether we can mutate
Each renderer would have its own local LegacyRefsObject function.
While in general we don't want `instanceof`, here it lets us do a simple check: did *we* create the refs object?
Then we can mutate it.
If the check didn't pass, either we're attaching ref for the first time (so we know to use the constructor),
or (unlikely) we're attaching a ref to a component owned by another renderer. In this case, to avoid "losing"
refs, we assign them onto the new object. Even in that case it shouldn't "hop" between renderers anymore.
* Clearer naming
* Add test case for strings refs across renderers
* Use a shared empty object for refs by reading it from React
* Remove string refs from ReactART test
It's not currently possible to resetModules() between several renderers
without also resetting the `React` module. However, that leads to losing
the referential identity of the empty ref object, and thus subsequent
checks in the renderers for whether it is pooled fail (and cause assignments
to a frozen object).
This has always been the case, but we used to work around it by shimming
fbjs/lib/emptyObject in tests and preserving its referential identity.
This won't work anymore because we've inlined it. And preserving referential
identity of React itself wouldn't be great because it could be confusing during
testing (although we might want to revisit this in the future by moving its
stateful parts into a separate package).
For now, I'm removing string ref usage from this test because only this is
the only place in our tests where we hit this problem, and it's only
related to string refs, and not just ref mechanism in general.
* Simplify the condition
2018-06-19 20:41:42 +08:00
|
|
|
instance.refs = emptyRefsObject;
|
2018-09-26 06:49:46 +08:00
|
|
|
|
2019-12-11 08:42:42 +08:00
|
|
|
initializeUpdateQueue(workInProgress);
|
|
|
|
|
|
2018-09-26 06:49:46 +08:00
|
|
|
const contextType = ctor.contextType;
|
2018-09-29 04:12:26 +08:00
|
|
|
if (typeof contextType === 'object' && contextType !== null) {
|
2018-10-17 02:58:00 +08:00
|
|
|
instance.context = readContext(contextType);
|
2019-08-02 08:21:32 +08:00
|
|
|
} else if (disableLegacyContext) {
|
|
|
|
|
instance.context = emptyContextObject;
|
2018-09-26 06:49:46 +08:00
|
|
|
} else {
|
|
|
|
|
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
|
|
|
|
instance.context = getMaskedContext(workInProgress, unmaskedContext);
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
if (__DEV__) {
|
2018-08-28 21:17:44 +08:00
|
|
|
if (instance.state === newProps) {
|
|
|
|
|
const componentName = getComponentName(ctor) || 'Component';
|
|
|
|
|
if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {
|
|
|
|
|
didWarnAboutDirectlyAssigningPropsToState.add(componentName);
|
2019-12-15 02:09:25 +08:00
|
|
|
console.error(
|
2018-08-28 21:17:44 +08:00
|
|
|
'%s: It is not recommended to assign props directly to state ' +
|
|
|
|
|
"because updates to props won't be reflected in state. " +
|
|
|
|
|
'In most cases, it is better to use props directly.',
|
|
|
|
|
componentName,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (workInProgress.mode & StrictMode) {
|
2018-05-23 06:38:02 +08:00
|
|
|
ReactStrictModeWarnings.recordLegacyContextWarning(
|
|
|
|
|
workInProgress,
|
|
|
|
|
instance,
|
|
|
|
|
);
|
2017-05-02 06:47:18 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (warnAboutDeprecatedLifecycles) {
|
2019-07-16 03:56:44 +08:00
|
|
|
ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
|
2018-05-19 18:29:11 +08:00
|
|
|
workInProgress,
|
|
|
|
|
instance,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-08 00:53:24 +08:00
|
|
|
|
2019-12-11 08:42:42 +08:00
|
|
|
processUpdateQueue(workInProgress, newProps, instance, renderExpirationTime);
|
|
|
|
|
instance.state = workInProgress.memoizedState;
|
2018-01-25 13:41:40 +08:00
|
|
|
|
2018-07-12 22:32:06 +08:00
|
|
|
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof getDerivedStateFromProps === 'function') {
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
applyDerivedStateFromProps(
|
|
|
|
|
workInProgress,
|
|
|
|
|
ctor,
|
|
|
|
|
getDerivedStateFromProps,
|
|
|
|
|
newProps,
|
|
|
|
|
);
|
2018-05-19 18:29:11 +08:00
|
|
|
instance.state = workInProgress.memoizedState;
|
|
|
|
|
}
|
2018-01-24 06:01:55 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
// In order to support react-lifecycles-compat polyfilled components,
|
|
|
|
|
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
|
|
|
|
if (
|
|
|
|
|
typeof ctor.getDerivedStateFromProps !== 'function' &&
|
|
|
|
|
typeof instance.getSnapshotBeforeUpdate !== 'function' &&
|
|
|
|
|
(typeof instance.UNSAFE_componentWillMount === 'function' ||
|
|
|
|
|
typeof instance.componentWillMount === 'function')
|
|
|
|
|
) {
|
|
|
|
|
callComponentWillMount(workInProgress, instance);
|
|
|
|
|
// If we had additional state updates during this life-cycle, let's
|
|
|
|
|
// process them now.
|
2019-12-11 08:42:42 +08:00
|
|
|
processUpdateQueue(
|
|
|
|
|
workInProgress,
|
|
|
|
|
newProps,
|
|
|
|
|
instance,
|
|
|
|
|
renderExpirationTime,
|
|
|
|
|
);
|
|
|
|
|
instance.state = workInProgress.memoizedState;
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof instance.componentDidMount === 'function') {
|
|
|
|
|
workInProgress.effectTag |= Update;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resumeMountClassInstance(
|
|
|
|
|
workInProgress: Fiber,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor: any,
|
|
|
|
|
newProps: any,
|
2018-05-19 18:29:11 +08:00
|
|
|
renderExpirationTime: ExpirationTime,
|
|
|
|
|
): boolean {
|
|
|
|
|
const instance = workInProgress.stateNode;
|
|
|
|
|
|
|
|
|
|
const oldProps = workInProgress.memoizedProps;
|
|
|
|
|
instance.props = oldProps;
|
|
|
|
|
|
|
|
|
|
const oldContext = instance.context;
|
2018-09-26 06:49:46 +08:00
|
|
|
const contextType = ctor.contextType;
|
2019-08-02 08:21:32 +08:00
|
|
|
let nextContext = emptyContextObject;
|
2018-09-29 04:12:26 +08:00
|
|
|
if (typeof contextType === 'object' && contextType !== null) {
|
2018-10-17 02:58:00 +08:00
|
|
|
nextContext = readContext(contextType);
|
2019-08-02 08:21:32 +08:00
|
|
|
} else if (!disableLegacyContext) {
|
2018-09-26 06:49:46 +08:00
|
|
|
const nextLegacyUnmaskedContext = getUnmaskedContext(
|
|
|
|
|
workInProgress,
|
|
|
|
|
ctor,
|
|
|
|
|
true,
|
|
|
|
|
);
|
|
|
|
|
nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
|
|
|
|
const hasNewLifecycles =
|
|
|
|
|
typeof getDerivedStateFromProps === 'function' ||
|
|
|
|
|
typeof instance.getSnapshotBeforeUpdate === 'function';
|
|
|
|
|
|
|
|
|
|
// Note: During these life-cycles, instance.props/instance.state are what
|
|
|
|
|
// ever the previously attempted to render - not the "current". However,
|
|
|
|
|
// during componentDidUpdate we pass the "current" props.
|
|
|
|
|
|
|
|
|
|
// In order to support react-lifecycles-compat polyfilled components,
|
|
|
|
|
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
|
|
|
|
if (
|
|
|
|
|
!hasNewLifecycles &&
|
|
|
|
|
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
|
|
|
|
|
typeof instance.componentWillReceiveProps === 'function')
|
|
|
|
|
) {
|
2018-09-26 06:49:46 +08:00
|
|
|
if (oldProps !== newProps || oldContext !== nextContext) {
|
2018-05-19 18:29:11 +08:00
|
|
|
callComponentWillReceiveProps(
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
workInProgress,
|
2018-05-19 18:29:11 +08:00
|
|
|
instance,
|
|
|
|
|
newProps,
|
2018-09-26 06:49:46 +08:00
|
|
|
nextContext,
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
);
|
2016-10-19 17:06:36 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
Decouple update queue from Fiber type (#12600)
* Decouple update queue from Fiber type
The update queue is in need of a refactor. Recent bugfixes (#12528) have
exposed some flaws in how it's modeled. Upcoming features like Suspense
and [redacted] also rely on the update queue in ways that weren't
anticipated in the original design.
Major changes:
- Instead of boolean flags for `isReplace` and `isForceUpdate`, updates
have a `tag` field (like Fiber). This lowers the cost for adding new
types of updates.
- Render phase updates are special cased. Updates scheduled during
the render phase are dropped if the work-in-progress does not commit.
This is used for `getDerivedStateFrom{Props,Catch}`.
- `callbackList` has been replaced with a generic effect list. Aside
from callbacks, this is also used for `componentDidCatch`.
* Remove first class UpdateQueue types and use closures instead
I tried to avoid this at first, since we avoid it everywhere else in the Fiber
codebase, but since updates are not in a hot path, the trade off with file size
seems worth it.
* Store captured errors on a separate part of the update queue
This way they can be reused independently of updates like
getDerivedStateFromProps. This will be important for resuming.
* Revert back to storing hasForceUpdate on the update queue
Instead of using the effect tag. Ideally, this would be part of the
return type of processUpdateQueue.
* Rename UpdateQueue effect type back to Callback
I don't love this name either, but it's less confusing than UpdateQueue
I suppose. Conceptually, this is usually a callback: setState callbacks,
componentDidCatch. The only case that feels a bit weird is Timeouts,
which use this effect to attach a promise listener. I guess that kinda
fits, too.
* Call getDerivedStateFromProps every render, even if props did not change
Rather than enqueue a new setState updater for every props change, we
can skip the update queue entirely and merge the result into state at
the end. This makes more sense, since "receiving props" is not an event
that should be observed. It's still a bit weird, since eventually we do
persist the derived state (in other words, it accumulates).
* Store captured effects on separate list from "own" effects (callbacks)
For resuming, we need the ability to discard the "own" effects while
reusing the captured effects.
* Optimize for class components
Change `process` and `callback` to match the expected payload types
for class components. I had intended for the update queue to be reusable
for both class components and a future React API, but we'll likely have
to fork anyway.
* Only double-invoke render phase lifecycles functions in DEV
* Use global state to track currently processing queue in DEV
2018-04-23 14:05:28 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
resetHasForceUpdateBeforeProcessing();
|
|
|
|
|
|
|
|
|
|
const oldState = workInProgress.memoizedState;
|
|
|
|
|
let newState = (instance.state = oldState);
|
2019-12-11 08:42:42 +08:00
|
|
|
processUpdateQueue(workInProgress, newProps, instance, renderExpirationTime);
|
|
|
|
|
newState = workInProgress.memoizedState;
|
2018-05-19 18:29:11 +08:00
|
|
|
if (
|
|
|
|
|
oldProps === newProps &&
|
|
|
|
|
oldState === newState &&
|
|
|
|
|
!hasContextChanged() &&
|
|
|
|
|
!checkHasForceUpdateAfterProcessing()
|
|
|
|
|
) {
|
|
|
|
|
// If an update was already in progress, we should schedule an Update
|
|
|
|
|
// effect even though we're bailing out, so that cWU/cDU are called.
|
2017-03-04 12:23:09 +08:00
|
|
|
if (typeof instance.componentDidMount === 'function') {
|
|
|
|
|
workInProgress.effectTag |= Update;
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
return false;
|
2016-10-19 17:06:36 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof getDerivedStateFromProps === 'function') {
|
|
|
|
|
applyDerivedStateFromProps(
|
|
|
|
|
workInProgress,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor,
|
2018-05-19 18:29:11 +08:00
|
|
|
getDerivedStateFromProps,
|
|
|
|
|
newProps,
|
|
|
|
|
);
|
|
|
|
|
newState = workInProgress.memoizedState;
|
|
|
|
|
}
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
const shouldUpdate =
|
|
|
|
|
checkHasForceUpdateAfterProcessing() ||
|
|
|
|
|
checkShouldComponentUpdate(
|
|
|
|
|
workInProgress,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor,
|
2018-05-19 18:29:11 +08:00
|
|
|
oldProps,
|
|
|
|
|
newProps,
|
|
|
|
|
oldState,
|
|
|
|
|
newState,
|
2018-09-26 06:49:46 +08:00
|
|
|
nextContext,
|
2018-05-19 18:29:11 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (shouldUpdate) {
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
// In order to support react-lifecycles-compat polyfilled components,
|
2018-03-27 04:28:10 +08:00
|
|
|
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
if (
|
2018-03-27 04:28:10 +08:00
|
|
|
!hasNewLifecycles &&
|
2018-05-19 18:29:11 +08:00
|
|
|
(typeof instance.UNSAFE_componentWillMount === 'function' ||
|
|
|
|
|
typeof instance.componentWillMount === 'function')
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
) {
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof instance.componentWillMount === 'function') {
|
|
|
|
|
instance.componentWillMount();
|
|
|
|
|
}
|
|
|
|
|
if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
|
|
|
|
instance.UNSAFE_componentWillMount();
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
}
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof instance.componentDidMount === 'function') {
|
|
|
|
|
workInProgress.effectTag |= Update;
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
} else {
|
|
|
|
|
// If an update was already in progress, we should schedule an Update
|
|
|
|
|
// effect even though we're bailing out, so that cWU/cDU are called.
|
|
|
|
|
if (typeof instance.componentDidMount === 'function') {
|
|
|
|
|
workInProgress.effectTag |= Update;
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
// If shouldComponentUpdate returned false, we should still update the
|
|
|
|
|
// memoized state to indicate that this work can be reused.
|
|
|
|
|
workInProgress.memoizedProps = newProps;
|
|
|
|
|
workInProgress.memoizedState = newState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the existing instance's state, props, and context pointers even
|
|
|
|
|
// if shouldComponentUpdate returns false.
|
|
|
|
|
instance.props = newProps;
|
|
|
|
|
instance.state = newState;
|
2018-09-26 06:49:46 +08:00
|
|
|
instance.context = nextContext;
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
return shouldUpdate;
|
|
|
|
|
}
|
2018-05-15 05:56:48 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
// Invokes the update life-cycles and returns false if it shouldn't rerender.
|
|
|
|
|
function updateClassInstance(
|
|
|
|
|
current: Fiber,
|
|
|
|
|
workInProgress: Fiber,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor: any,
|
|
|
|
|
newProps: any,
|
2018-05-19 18:29:11 +08:00
|
|
|
renderExpirationTime: ExpirationTime,
|
|
|
|
|
): boolean {
|
|
|
|
|
const instance = workInProgress.stateNode;
|
|
|
|
|
|
2019-12-11 08:42:42 +08:00
|
|
|
cloneUpdateQueue(current, workInProgress);
|
|
|
|
|
|
2020-04-08 17:58:57 +08:00
|
|
|
const unresolvedOldProps = workInProgress.memoizedProps;
|
|
|
|
|
const oldProps =
|
2018-11-07 03:54:14 +08:00
|
|
|
workInProgress.type === workInProgress.elementType
|
2020-04-08 17:58:57 +08:00
|
|
|
? unresolvedOldProps
|
|
|
|
|
: resolveDefaultProps(workInProgress.type, unresolvedOldProps);
|
|
|
|
|
instance.props = oldProps;
|
|
|
|
|
const unresolvedNewProps = workInProgress.pendingProps;
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
const oldContext = instance.context;
|
2018-09-26 06:49:46 +08:00
|
|
|
const contextType = ctor.contextType;
|
2019-08-02 08:21:32 +08:00
|
|
|
let nextContext = emptyContextObject;
|
2018-09-29 04:12:26 +08:00
|
|
|
if (typeof contextType === 'object' && contextType !== null) {
|
2018-10-17 02:58:00 +08:00
|
|
|
nextContext = readContext(contextType);
|
2019-08-02 08:21:32 +08:00
|
|
|
} else if (!disableLegacyContext) {
|
2018-09-26 06:49:46 +08:00
|
|
|
const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
|
|
|
|
nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
|
|
|
|
const hasNewLifecycles =
|
|
|
|
|
typeof getDerivedStateFromProps === 'function' ||
|
|
|
|
|
typeof instance.getSnapshotBeforeUpdate === 'function';
|
|
|
|
|
|
|
|
|
|
// Note: During these life-cycles, instance.props/instance.state are what
|
|
|
|
|
// ever the previously attempted to render - not the "current". However,
|
|
|
|
|
// during componentDidUpdate we pass the "current" props.
|
|
|
|
|
|
|
|
|
|
// In order to support react-lifecycles-compat polyfilled components,
|
|
|
|
|
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
|
|
|
|
if (
|
|
|
|
|
!hasNewLifecycles &&
|
|
|
|
|
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
|
|
|
|
|
typeof instance.componentWillReceiveProps === 'function')
|
|
|
|
|
) {
|
2020-04-08 17:58:57 +08:00
|
|
|
if (
|
|
|
|
|
unresolvedOldProps !== unresolvedNewProps ||
|
|
|
|
|
oldContext !== nextContext
|
|
|
|
|
) {
|
2018-05-19 18:29:11 +08:00
|
|
|
callComponentWillReceiveProps(
|
2018-05-15 10:18:47 +08:00
|
|
|
workInProgress,
|
2018-05-19 18:29:11 +08:00
|
|
|
instance,
|
2018-05-15 10:18:47 +08:00
|
|
|
newProps,
|
2018-09-26 06:49:46 +08:00
|
|
|
nextContext,
|
2018-05-15 10:18:47 +08:00
|
|
|
);
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
|
|
|
|
}
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
resetHasForceUpdateBeforeProcessing();
|
|
|
|
|
|
|
|
|
|
const oldState = workInProgress.memoizedState;
|
|
|
|
|
let newState = (instance.state = oldState);
|
2019-12-11 08:42:42 +08:00
|
|
|
processUpdateQueue(workInProgress, newProps, instance, renderExpirationTime);
|
|
|
|
|
newState = workInProgress.memoizedState;
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
if (
|
2020-04-08 17:58:57 +08:00
|
|
|
unresolvedOldProps === unresolvedNewProps &&
|
2018-05-19 18:29:11 +08:00
|
|
|
oldState === newState &&
|
|
|
|
|
!hasContextChanged() &&
|
|
|
|
|
!checkHasForceUpdateAfterProcessing()
|
|
|
|
|
) {
|
|
|
|
|
// If an update was already in progress, we should schedule an Update
|
|
|
|
|
// effect even though we're bailing out, so that cWU/cDU are called.
|
|
|
|
|
if (typeof instance.componentDidUpdate === 'function') {
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
if (
|
2020-04-08 17:58:57 +08:00
|
|
|
unresolvedOldProps !== current.memoizedProps ||
|
2018-05-19 18:29:11 +08:00
|
|
|
oldState !== current.memoizedState
|
Add stack unwinding phase for handling errors (#12201)
* Add stack unwinding phase for handling errors
A rewrite of error handling, with semantics that more closely match
stack unwinding.
Errors that are thrown during the render phase unwind to the nearest
error boundary, like before. But rather than synchronously unmount the
children before retrying, we restart the failed subtree within the same
render phase. The failed children are still unmounted (as if all their
keys changed) but without an extra commit.
Commit phase errors are different. They work by scheduling an error on
the update queue of the error boundary. When we enter the render phase,
the error is popped off the queue. The rest of the algorithm is
the same.
This approach is designed to work for throwing non-errors, too, though
that feature is not implemented yet.
* Add experimental getDerivedStateFromCatch lifecycle
Fires during the render phase, so you can recover from an error within the same
pass. This aligns error boundaries more closely with try-catch semantics.
Let's keep this behind a feature flag until a future release. For now, the
recommendation is to keep using componentDidCatch. Eventually, the advice will
be to use getDerivedStateFromCatch for handling errors and componentDidCatch
only for logging.
* Reconcile twice to remount failed children, instead of using a boolean
* Handle effect immediately after its thrown
This way we don't have to store the thrown values on the effect list.
* ReactFiberIncompleteWork -> ReactFiberUnwindWork
* Remove startTime
* Remove TypeOfException
We don't need it yet. We'll reconsider once we add another exception type.
* Move replay to outer catch block
This moves it out of the hot path.
2018-02-24 09:38:42 +08:00
|
|
|
) {
|
|
|
|
|
workInProgress.effectTag |= Update;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
|
|
|
|
if (
|
2020-04-08 17:58:57 +08:00
|
|
|
unresolvedOldProps !== current.memoizedProps ||
|
2018-05-19 18:29:11 +08:00
|
|
|
oldState !== current.memoizedState
|
|
|
|
|
) {
|
|
|
|
|
workInProgress.effectTag |= Snapshot;
|
2018-01-30 00:06:50 +08:00
|
|
|
}
|
2016-10-19 17:06:36 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
2016-10-19 17:06:36 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof getDerivedStateFromProps === 'function') {
|
2018-06-12 07:31:07 +08:00
|
|
|
applyDerivedStateFromProps(
|
|
|
|
|
workInProgress,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor,
|
2018-06-12 07:31:07 +08:00
|
|
|
getDerivedStateFromProps,
|
|
|
|
|
newProps,
|
|
|
|
|
);
|
|
|
|
|
newState = workInProgress.memoizedState;
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
2016-10-19 17:06:36 +08:00
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
const shouldUpdate =
|
|
|
|
|
checkHasForceUpdateAfterProcessing() ||
|
|
|
|
|
checkShouldComponentUpdate(
|
|
|
|
|
workInProgress,
|
Accept promise as element type (#13397)
* Accept promise as element type
On the initial render, the element will suspend as if a promise were
thrown from inside the body of the unresolved component. Siblings should
continue rendering and if the parent is a Placeholder, the promise
should be captured by that Placeholder.
When the promise resolves, rendering resumes. If the resolved value
has a `default` property, it is assumed to be the default export of
an ES module, and we use that as the component type. If it does not have
a `default` property, we use the resolved value itself.
The resolved value is stored as an expando on the promise/thenable.
* Use special types of work for lazy components
Because reconciliation is a hot path, this adds ClassComponentLazy,
FunctionalComponentLazy, and ForwardRefLazy as special types of work.
The other types are not supported, but wouldn't be placed into a
separate module regardless.
* Resolve defaultProps for lazy types
* Remove some calls to isContextProvider
isContextProvider checks the fiber tag, but it's typically called after
we've already refined the type of work. We should get rid of it. I
removed some of them in the previous commit, and deleted a few more
in this one. I left a few behind because the remaining ones would
require additional refactoring that feels outside the scope of this PR.
* Remove getLazyComponentTypeIfResolved
* Return baseProps instead of null
The caller compares the result to baseProps to see if anything changed.
* Avoid redundant checks by inlining getFiberTagFromObjectType
* Move tag resolution to ReactFiber module
* Pass next props to update* functions
We should do this with all types of work in the future.
* Refine component type before pushing/popping context
Removes unnecessary checks.
* Replace all occurrences of _reactResult with helper
* Move shared thenable logic to `shared` package
* Check type of wrapper object before resolving to `default` export
* Return resolved tag instead of reassigning
2018-08-17 00:21:59 +08:00
|
|
|
ctor,
|
2018-05-19 18:29:11 +08:00
|
|
|
oldProps,
|
|
|
|
|
newProps,
|
|
|
|
|
oldState,
|
|
|
|
|
newState,
|
2018-09-26 06:49:46 +08:00
|
|
|
nextContext,
|
2018-05-19 18:29:11 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (shouldUpdate) {
|
|
|
|
|
// In order to support react-lifecycles-compat polyfilled components,
|
|
|
|
|
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
2017-03-14 08:05:18 +08:00
|
|
|
if (
|
2018-05-19 18:29:11 +08:00
|
|
|
!hasNewLifecycles &&
|
|
|
|
|
(typeof instance.UNSAFE_componentWillUpdate === 'function' ||
|
|
|
|
|
typeof instance.componentWillUpdate === 'function')
|
2017-03-14 08:05:18 +08:00
|
|
|
) {
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof instance.componentWillUpdate === 'function') {
|
2018-09-26 06:49:46 +08:00
|
|
|
instance.componentWillUpdate(newProps, newState, nextContext);
|
2017-03-04 12:23:09 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
2018-09-26 06:49:46 +08:00
|
|
|
instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
|
2018-03-27 04:28:10 +08:00
|
|
|
}
|
2016-11-02 08:41:36 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof instance.componentDidUpdate === 'function') {
|
|
|
|
|
workInProgress.effectTag |= Update;
|
2018-05-15 05:56:48 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
|
|
|
|
workInProgress.effectTag |= Snapshot;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// If an update was already in progress, we should schedule an Update
|
|
|
|
|
// effect even though we're bailing out, so that cWU/cDU are called.
|
|
|
|
|
if (typeof instance.componentDidUpdate === 'function') {
|
2018-01-20 01:36:46 +08:00
|
|
|
if (
|
2020-04-08 17:58:57 +08:00
|
|
|
unresolvedOldProps !== current.memoizedProps ||
|
2018-05-19 18:29:11 +08:00
|
|
|
oldState !== current.memoizedState
|
2018-01-20 01:36:46 +08:00
|
|
|
) {
|
2017-03-04 12:23:09 +08:00
|
|
|
workInProgress.effectTag |= Update;
|
|
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
}
|
|
|
|
|
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
|
|
|
|
if (
|
2020-04-08 17:58:57 +08:00
|
|
|
unresolvedOldProps !== current.memoizedProps ||
|
2018-05-19 18:29:11 +08:00
|
|
|
oldState !== current.memoizedState
|
|
|
|
|
) {
|
2018-03-27 04:28:10 +08:00
|
|
|
workInProgress.effectTag |= Snapshot;
|
|
|
|
|
}
|
2016-10-19 17:06:36 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
// If shouldComponentUpdate returned false, we should still update the
|
|
|
|
|
// memoized props/state to indicate that this work can be reused.
|
|
|
|
|
workInProgress.memoizedProps = newProps;
|
|
|
|
|
workInProgress.memoizedState = newState;
|
2016-10-12 01:43:47 +08:00
|
|
|
}
|
|
|
|
|
|
2018-05-19 18:29:11 +08:00
|
|
|
// Update the existing instance's state, props, and context pointers even
|
|
|
|
|
// if shouldComponentUpdate returns false.
|
|
|
|
|
instance.props = newProps;
|
|
|
|
|
instance.state = newState;
|
2018-09-26 06:49:46 +08:00
|
|
|
instance.context = nextContext;
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
return shouldUpdate;
|
2017-11-03 03:50:03 +08:00
|
|
|
}
|
2018-05-19 18:29:11 +08:00
|
|
|
|
|
|
|
|
export {
|
|
|
|
|
adoptClassInstance,
|
|
|
|
|
constructClassInstance,
|
|
|
|
|
mountClassInstance,
|
|
|
|
|
resumeMountClassInstance,
|
|
|
|
|
updateClassInstance,
|
|
|
|
|
};
|