The `enableHooks` feature flag used to only control whether the API
was exposed on the React package. But now it also determines if the
dispatcher and implementation are included in the bundle.
We're using hooks in www, so I've switched the feature flag to `true`
in the www build.
(Alternatively, we could have two feature flags: one for the
implementation and dispatcher, and one for exposing the API on the
React package.)
Removes the `enableDispatchCallback` feature flag and deletes the
associated code. An earlier version of the Hooks proposal included this
feature but we've since decided to remove it.
* [scheduler] Deadline object -> shouldYield
Instead of using a requestIdleCallback-style deadline object, expose a
method Scheduler.shouldYield that returns true if there's a higher
priority event in the queue.
* Nits
Effects scheduled by useEffect should not fire until after the browser
has had a chance to paint. However, they should be fired before any
subsequent mutations.
Also adds useMutationEffect and useLayoutEffect. useMutationEffect fires
during the host update phase. useLayoutEffect fires during the post-
update phase (the same phase as componentDidMount
and componentDidUpdate).
* [Synchronous Suspense] Suspending a class outside concurrent mode
When a class component suspends during mount outside concurrent mode,
change the tag so it's not mistaken for a completed component. For
example, we should not call componentWillUnmount if it is deleted.
* PR nits
* Allow arbitrary types to be wrapped in pure
This creates an outer fiber that container the pure check and an inner
fiber that represents which ever type of component.
* Add optimized fast path for simple pure function components
Special cased when there are no defaultProps and it's a simple function
component instead of class. This doesn't require an extra fiber.
We could make it so that this also works with custom comparer but that
means we have to go through one extra indirection to get to it.
Maybe it's worth it, donno.
* Introduce elementType field
This will be used to store the wrapped type of an element. E.g. pure and
lazy.
The existing type field will be used for the unwrapped type within them.
* Store the unwrapped type on the type field of lazy components
* Use the raw tags for lazy components
Instead, we check if the elementType and type are equal to test if
we need to resolve props. This is slightly slower in the normal case
but will yield less code and branching.
* Clean up lazy branches
* Collapse work tag numbering
* Split IndeterminateComponent out from Lazy
This way we don't have to check the type in a hacky way in the
indeterminate path. Also, lets us deal with lazy that resolves to
indeterminate and such.
* Missing clean up in rebase
Removes support for using arbitrary promises as the type of a React
element. Instead, promises must be wrapped in React.lazy. This gives us
flexibility later if we need to change the protocol.
The reason is that promises do not provide a way to call their
constructor multiple times. For example:
const promiseForA = new Promise(resolve => {
fetchA(a => resolve(a));
});
Given a reference to `promiseForA`, there's no way to call `fetchA`
again. Calling `then` on the promise doesn't run the constructor again;
it only attaches another listener.
In the future we will likely introduce an API like `React.eager` that
is similar to `lazy` but eagerly calls the constructor. That gives us
the ability to call the constructor multiple times. E.g. to increase
the priority, or to retry if the first operation failed.
* Store the start time on `updateQueue` instead of `stateNode`
Originally I did this to free the `stateNode` field to store a second
set of children. I don't we'll need this anymore, since we use fragment
fibers instead. But I still think using `updateQueue` makes more sense
so I'll leave this in.
* Use fragment fibers to keep the primary and fallback children separate
If the children timeout, we switch to showing the fallback children in
place of the "primary" children. However, we don't want to delete the
primary children because then their state will be lost (both the React
state and the host state, e.g. uncontrolled form inputs). Instead we
keep them mounted and hide them. Both the fallback children AND the
primary children are rendered at the same time. Once the primary
children are un-suspended, we can delete the fallback children — don't
need to preserve their state.
The two sets of children are siblings in the host environment, but
semantically, for purposes of reconciliation, they are two separate
sets. So we store them using two fragment fibers.
However, we want to avoid allocating extra fibers for every placeholder.
They're only necessary when the children time out, because that's the
only time when both sets are mounted.
So, the extra fragment fibers are only used if the children time out.
Otherwise, we render the primary children directly. This requires some
custom reconciliation logic to preserve the state of the primary
children. It's essentially a very basic form of re-parenting.
* Use `memoizedState` to store various pieces of SuspenseComponent's state
SuspenseComponent has three pieces of state:
- alreadyCaptured: Whether a component in the child subtree already
suspended. If true, subsequent suspends should bubble up to the
next boundary.
- didTimeout: Whether the boundary renders the primary or fallback
children. This is separate from `alreadyCaptured` because outside of
strict mode, when a boundary times out, the first commit renders the
primary children in an incomplete state, then performs a second commit
to switch the fallback. In that first commit, `alreadyCaptured` is
false and `didTimeout` is true.
- timedOutAt: The time at which the boundary timed out. This is separate
from `didTimeout` because it's not set unless the boundary
actually commits.
These were previously spread across several fields.
This happens to make the non-strict case a bit less hacky; the logic for
that special case is now mostly localized to the UnwindWork module.
* Hide timed-out Suspense children
When a subtree takes too long to load, we swap its contents out for
a fallback to unblock the rest of the tree. Because we don't want
to lose the state of the timed out view, we shouldn't actually delete
the nodes from the tree. Instead, we'll keep them mounted and hide
them visually. When the subtree is unblocked, we un-hide it, having
preserved the existing state.
Adds additional host config methods. For mutation mode:
- hideInstance
- hideTextInstance
- unhideInstance
- unhideTextInstance
For persistent mode:
- cloneHiddenInstance
- cloneUnhiddenInstance
- createHiddenTextInstance
I've only implemented the new methods in the noop and test renderers.
I'll implement them in the other renderers in subsequent commits.
* Include `hidden` prop in noop renderer's output
This will be used in subsequent commits to test that timed-out children
are properly hidden.
Also adds getChildrenAsJSX() method as an alternative to using
getChildren(). (Ideally all our tests would use test renderer #oneday.)
* Implement hide/unhide host config methods for DOM renderer
For DOM nodes, we hide using `el.style.display = 'none'`.
Text nodes don't have style, so we hide using `text.textContent = ''`.
* Implement hide/unhide host config methods for Art renderer
* Create DOM fixture that tests state preservation of timed out content
* Account for class components that suspend outside concurrent mode
Need to distinguish mount from update. An unfortunate edge case :(
* Fork appendAllChildren between persistent and mutation mode
* Remove redundant check for existence of el.style
* Schedule placement effect on indeterminate components
In non-concurrent mode, indeterminate fibers may commit in an
inconsistent state. But when they update, we should throw out the
old fiber and start fresh. Which means the new fiber needs a
placement effect.
* Pass null instead of current everywhere in mountIndeterminateComponent
* Removed the enableGetDerivedStateFromCatch feature flag (aka permanently enabled the feature)
* Forked/copied ReactErrorBoundaries to ReactLegacyErrorBoundaries for testing componentDidCatch
* Updated error boundaries tests to apply to getDerivedStateFromCatch
* Renamed getDerivedStateFromCatch -> getDerivedStateFromError
* Warn if boundary with only componentDidCatch swallows error
* Fixed a subtle reconciliation bug with render phase error boundary
* pure
A higher-order component version of the `React.PureComponent` class.
During an update, the previous props are compared to the new props. If
they are the same, React will skip rendering the component and
its children.
Unlike userspace implementations, `pure` will not add an additional
fiber to the tree.
The first argument must be a functional component; it does not work
with classes.
`pure` uses shallow comparison by default, like `React.PureComponent`.
A custom comparison can be passed as the second argument.
Co-authored-by: Andrew Clark <acdlite@fb.com>
Co-authored-by: Sophie Alpert <sophiebits@fb.com>
* Warn if first argument is not a functional component
All of these features are based on features of React's internal
scheduler. The eventual goal is to lift as much as possible out of the
React internals into the Scheduler package.
Includes some renaming of existing methods.
- `scheduleWork` is now `scheduleCallback`
- `cancelScheduledWork` is now `cancelCallback`
Priority levels
---------------
Adds the ability to schedule callbacks at different priority levels.
The current levels are (final names TBD):
- Immediate priority. Fires at the end of the outermost currently
executing (similar to a microtask).
- Interactive priority. Fires within a few hundred milliseconds. This
should only be used to provide quick feedback to the user as a result
of an interaction.
- Normal priority. This is the default. Fires within several seconds.
- "Maybe" priority. Only fires if there's nothing else to do. Used for
prerendering or warming a cache.
The priority is changed using `runWithPriority`:
```js
runWithPriority(InteractivePriority, () => {
scheduleCallback(callback);
});
```
Continuations
-------------
Adds the ability for a callback to yield without losing its place
in the queue, by returning a continuation. The continuation will have
the same expiration as the callback that yielded.
Wrapped callbacks
-----------------
Adds the ability to wrap a callback so that, when it is called, it
receives the priority of the current execution context.
* Refactor Schedule, remove React-isms
Once the API stabilizes, we will move Schedule this into a separate
repo. To promote adoption, especially by projects outside the React
ecosystem, we'll remove all React-isms from the source and keep it as
simple as possible:
- No build step.
- No static types.
- Everything is in a single file.
If we end up needing to support multiple targets, like CommonJS and ESM,
we can still avoid a build step by maintaining two copies of the same
file, but with different exports.
This commit also refactors the implementation to split out the DOM-
specific parts (essentially a requestIdleCallback polyfill). Aside from
the architectural benefits, this also makes it possible to write host-
agnostic tests. If/when we publish a version of Schedule that targets
other environments, like React Native, we can run these same tests
across all implementations.
* Edits in response to Dan's PR feedback
* 🔥 Stop syncing the value attribute on inputs
* Eliminate some additional checks
* Remove initialValue and initialWrapper from wrapperState flow type
* Update tests with new sync logic, reduce some operations
* Update tests, add some caveats for SSR mismatches
* Revert newline change
* Remove unused type
* Call toString to safely type string values
* Add disableInputAttributeSyncing feature flag
Reverts tests to original state, adds attribute sync feature flag,
then moves all affected tests to ReactFire-test.js.
* Revert position of types in toStringValues
* Invert flag on number input blur
* Add clarification why double blur is necessary
* Update ReactFire number cases to be more explicite about blur
* Move comments to reduce diff size
* Add comments to clarify behavior in each branch
* There is no need to assign a different checked behavior in Fire
* Use checked reference
* Format
* Avoid precomputing stringable values
* Revert getToStringValue comment
* Revert placement of undefined in getToStringValue
* Do not eagerly stringify value
* Unify Fire test cases with normal ones
* Revert toString change. Only assign unsynced values when not nully
* Fix warning without stack for ie9
Where console methods like log, error etc. don't have 'apply' method.
Because of the lot of tests already expect that exactly console['method']
will be called - had to reapply references for console.error method
https://github.com/facebook/react/issues/13610
* pass parameters explicitly to avoid using .apply
which is not supported for console methods in ie9
* Minor tweaks
* add failing test
* honor displayName set on ForwardRef if available
Since React.forwardRef returns a component object, some users
(including styled-components and react-native) are starting to
decorate them with various statics including displayName.
This adjusts React's various name-getters to honor this if set and
surface the name in warnings and hopefully DevTools.
* fix typing
* Refine later