* Add SuspenseList component type
* Push SuspenseContext for SuspenseList
* Force Suspense boundaries into their fallback state
In the "together" mode, we do a second render pass that forces the
fallbacks to stay in place, if not all can unsuspend at once.
* Add test
* Transfer thennables to the SuspenseList
This way, we end up retrying the SuspenseList in case the nested boundary
that just suspended doesn't actually get mounted with this set of
thennables. This happens when the second pass renders the fallback
directly without first attempting to render the content.
* Add warning for unsupported displayOrder
* Add tests for nested sibling boundaries and nested lists
* Fix nested SuspenseList forwarding thennables
* Rename displayOrder to revealOrder
Display order has some "display list" connotations making it sound like
a z-index thing.
Reveal indicates that this isn't really about when something gets rendered
or is ready to be rendered. It's about when content that is already there
gets to be revealed.
* Add test for avoided boundaries
* Make SuspenseList a noop in legacy mode
* Use an explicit suspense list state object
This will be used for more things in the directional case.
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.
* 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
* 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
Changed the API to match what we've been using in our latest discussions.
Our tentative plans are for <Placeholder> to automatically hide the timed-out
children, instead of removing them, so their state is not lost. This part is
not yet implemented. We'll likely have a lower level API that does not include
the hiding behavior. This is also not yet implemented.
* Timeout component
Adds Timeout component. If a promise is thrown from inside a Timeout component,
React will suspend the in-progress render from committing. When the promise
resolves, React will retry. If the render is suspended for longer than the
maximum threshold, the Timeout switches to a placeholder state.
The timeout threshold is defined as the minimum of:
- The expiration time of the current render
- The `ms` prop given to each Timeout component in the ancestor path of the
thrown promise.
* Add a test for nested fallbacks
Co-authored-by: Andrew Clark <acdlite@fb.com>
* Resume on promise rejection
React should resume rendering regardless of whether it resolves
or rejects.
* Wrap Suspense code in feature flag
* Children of a Timeout must be strict mode compatible
Async is not required for Suspense, but strict mode is.
* Simplify list of pending work
Some of this was added with "soft expiration" in mind, but now with our revised
model for how soft expiration will work, this isn't necessary.
It would be nice to remove more of this, but I think the list itself is inherent
because we need a way to track the start times, for <Timeout ms={ms} />.
* Only use the Timeout update queue to store promises, not for state
It already worked this way in practice.
* Wrap more Suspense-only paths in the feature flag
* Attach promise listener immediately on suspend
Instead of waiting for commit phase.
* Infer approximate start time using expiration time
* Remove list of pending priority levels
We can replicate almost all the functionality by tracking just five
separate levels: the highest/lowest priority pending levels, the
highest/lowest priority suspended levels, and the lowest pinged level.
We lose a bit of granularity, in that if there are multiple levels of
pending updates, only the first and last ones are known. But in practice
this likely isn't a big deal.
These heuristics are almost entirely isolated to a single module and
can be adjusted later, without API changes, if necessary.
Non-IO-bound work is not affected at all.
* ReactFiberPendingWork -> ReactFiberPendingPriority
* Renaming method names from "pending work" to "pending priority"
* Get rid of SuspenseThenable module
Idk why I thought this was neccessary
* Nits based on Sebastian's feedback
* More naming nits + comments
* Add test for hiding a suspended tree to unblock
* Revert change to expiration time rounding
This means you have to account for the start time approximation
heuristic when writing Suspense tests, but that's going to be
true regardless.
When updating the tests, I also made a fix related to offscreen
priority. We should never timeout inside a hidden tree.
* palceholder -> placeholder
Add a new component type, Profiler, that can be used to collect new render time metrics. Since this is a new, experimental API, it will be exported as React.unstable_Profiler initially.
Most of the functionality for this component has been added behind a feature flag, enableProfileModeMetrics. When the feature flag is disabled, the component will just render its children with no additional behavior. When the flag is enabled, React will also collect timing information and pass it to the onRender function (as described below).
* Add React.isValidElementType()
Per the conversation on #12453, there are a number of third-party
libraries (particularly those that generate higher-order components)
that are performing suboptimal validation of element types.
This commit exposes a function that can perform the desired check
without depending upon React internals.
* Move isValidElementType to shared/