<time> tag has been supported by Chrome since Chrome 62.0.
Remove workarounds which were in place to avoid friction with
versions before Chrome 62.
Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
Technically this change is unnecessary, since the feature is controlled by a flag, but since we decided not to ship this in v17– I'm going to remove it for now entirely.
When a link opens a URL in a new tab with target="_blank", it is very simple for the opened page to change the location of the original page because the JavaScript variable window.opener is not null and thus "window.opener.location can be set by the opened page. This exposes the user to very simple phishing attacks.
Adds a new prop to the Suspense component type,
`unstable_expectedLoadTime`. The presence of this prop indicates that
the content is computationally expensive to render.
During the initial mount, React will skip over expensive trees by
rendering a placeholder — just like we do with trees that are waiting
for data to resolve. That will help unblock the initial skeleton for the
new screen. Then we will continue rendering in the next commit.
For now, while we experiment with the API internally, any number passed
to `unstable_expectedLoadTime` will be treated as "computationally
expensive", no matter how large or small. So it's basically a boolean.
The reason it's a number is that, in the future, we may try to be clever
with this additional information. For example, SuspenseList could use
it as part of its heuristic to determine whether to keep rendering
additional rows.
Background
----------
Much of our early messaging and research into Suspense focused on its
ability to throttle the appearance of placeholder UIs. Our theory was
that, on a fast network, if everything loads quickly, excessive
placeholders will contribute to a janky user experience. This was backed
up by user research and has held up in practice.
However, our original demos made an even stronger assertion: not only is
it preferable to throttle successive loading states, but up to a certain
threshold, it’s also preferable to remain on the previous screen; or in
other words, to delay the transition.
This strategy has produced mixed results. We’ve found it works well for
certain transitions, but not for all them. When performing a full page
transition, showing an initial skeleton as soon as possible is crucial
to making the transition feel snappy. You still want throttle the nested
loading states as they pop in, but you need to show something on the new
route. Remaining on the previous screen can make the app feel
unresponsive.
That’s not to say that delaying the previous screen always leads to a
bad user experience. Especially if you can guarantee that the delay is
small enough that the user won’t notice it. This threshold is a called a
Just Noticeable Difference (JND). If we can stay under the JND, then
it’s worth skipping the first placeholder to reduce overall thrash.
Delays that are larger than the JND have some use cases, too. The main
one we’ve found is to refresh existing data, where it’s often preferable
to keep stale content on screen while the new data loads in the
background. It’s also useful as a fallback strategy if something
suspends unexpectedly, to avoid hiding parts of the UI that are already
visible.
We’re still in the process of optimizing our heuristics for the most
common patterns. In general, though, we are trending toward being more
aggressive about prioritizing the initial skeleton.
For example, Suspense is usually thought of as a feature for displaying
placeholders when the UI is missing data — that is, when rendering is
bound by pending IO.
But it turns out that the same principles apply to CPU-bound
transitions, too. It’s worth deferring a tree that’s slow to render if
doing so unblocks the rest of the transition — regardless of whether
it’s slow because of missing data or because of expensive CPU work.
We already take advantage of this idea in a few places, such as
hydration. Instead of hydrating server-rendered UI in a single pass,
React splits it into chunks. It can do this because the initial HTML
acts as its own placeholder. React can defer hydrating a chunk of UI as
long as it wants until the user interacts it. The boundary we use to
split the UI into chunks is the same one we use for IO-bound subtrees:
the <Suspense /> component.
SuspenseList does something similar. When streaming in a list of items,
it will occasionally stop to commit whatever items have already
finished, before continuing where it left off. It does this by showing a
placeholder for the remaining items, again using the same <Suspense />
component API, even if the item is CPU-bound.
Unresolved questions
--------------------
There is a concern that showing a placeholder without also loading new
data could be disorienting. Users are trained to believe that a
placeholder signals fresh content. So there are still some questions
we’ll need to resolve.
Commit phase durations (layout and passive) are stored on the nearest (ancestor) Profiler and bubble up during the commit phase. This bubbling used to be implemented by traversing the return path each time we finished working on a Profiler to find the next nearest Profiler.
This commit removes that traversal. Instead, we maintain a stack of nearest Profiler ancestor while recursing the tree. This stack is maintained in the work loop (since that's where the recursive functions are) and so bubbling of durations has also been moved from commit-work to the work loop.
This PR also refactors the methods used to recurse and apply effects in preparation for the new Offscreen component type.
This PR double invokes effects in __DEV__ mode.
We are thinking about unmounting layout and/or passive effects for a hidden tree. To understand potential issues with this, we want to double invoke effects. This PR changes the behavior in DEV when an effect runs from create() to create() -> destroy() -> create(). The effect cleanup function will still be called before the effect runs in both dev and prod. (Note: This change is purely for research for now as it is likely to break real code.)
**Note: The change is fully behind a flag and does not affect any of the code on npm.**
* Don't call onCommit et al if there are no effects
Checks `subtreeFlags` before scheduling an effect on the Profiler.
* Fix failing Profiler tests
The change to conditionally call Profiler commit hooks only if updates were scheduled broke a few of the Profiler tests. I've fixed the tests by either:
* Adding a no-op passive effect into the subtree or
* Converting onPostCommit to onCommit
When possible, I opted to add the no-op passive effect to the tests since that that hook is called later (during passive phase) so the test is a little broader. In a few cases, this required adding awkward act() wrappers so I opted to go with onCommit instead.
Co-authored-by: Brian Vaughn <bvaughn@fb.com>
There were a few pairs of commit phase functions that were almost
identical except for one detail. I've refactored them a bit to
consolidate their implementations:
- Lifted error handling logic when mounting a fiber's passive hook
effects to surround the entire list, instead of surrounding each effect.
- Lifted profiler duration tracking to surround the entire list.
In both cases, this matches the corresponding code for the layout phase.
The naming is still a bit of a mess but I'm not too concerned because
my next step is to refactor each commit sub-phase (layout, mutation)
so that we can store values on the JS stack. So the existing function
boundaries are about to change, anyway.
DevTools shared Babel config previously supported IE 11 to target Hermes (for the standalone backend that gets embedded within React Native apps). This targeting resulted in less optimal code for other DevTools targets though which did not need to support IE 11. This PR updates the shared config to remove IE 11 support by default, and only enables it for the standalone backend target.
Instead of calling `onPostCommit` in a separate phase, we can fire
them during the same traversal as the rest of the passive effects.
This works because effects are executed depth-first. So by the time we
reach a Profiler node, we'll have already executed all the effects in
its subtree.
* Improve DevTools editing interface
This commit adds the ability to rename or delete keys in the props/state/hooks/context editor and adds tests to cover this functionality. DevTools will degrade gracefully for older versions of React that do not inject the new reconciler rename* or delete* methods.
Specifically, this commit includes the following changes:
* Adds unit tests (for modern and legacy renderers) to cover overriding props, renaming keys, and deleting keys.
* Refactor backend override methods to reduce redundant Bridge/Agent listeners and methods.
* Inject new (DEV-only) methods from reconciler into DevTools to rename and delete paths.
* Refactor 'inspected element' UI components to improve readability.
* Improve auto-size input to better mimic Chrome's Style editor panel. (See this Code Sandbox for a proof of concept.)
It also contains the following code cleanup:
* Additional unit tests have been added for modifying values as well as renaming or deleting paths.
* Four new DEV-only methods have been added to the reconciler to be injected into the DevTools hook: overrideHookStateDeletePath, overrideHookStateRenamePath, overridePropsDeletePath, and overridePropsRenamePath. (DevTools will degrade gracefully for older renderers without these methods.)
* I also took this as an opportunity to refactor some of the existing code in a few places:
* Rather than the backend implementing separate methods for editing props, state, hooks, and context– there are now three methods: deletePath, renamePath, and overrideValueAtPath that accept a type argument to differentiate between props, state, context, or hooks.
* The various UI components for the DevTools frontend have been refactored to remove some unnecessary repetition.
This commit also adds temporary support for override* commands with mismatched backend/frontend versions:
* Add message forwarding for older backend methods (overrideContext, overrideHookState, overrideProps, and overrideState) to the new overrideValueAtPath method. This was done in both the frontend Bridge (for newer frontends passing messages to older embedded backends) and in the backend Agent (for older frontends passing messages to newer backends). We do this because React Native embeds the React DevTools backend, but cannot control which version of the frontend users use.
* Additional unit tests have been added as well to cover the older frontend to newer backend case. Our DevTools test infra does not make it easy to write tests for the other way around.
Adds back the `TestUtils.act` implementation that I had removed
in #19745. This version of `act` is implemented in "userspace" (i.e. not
the reconciler), so it doesn't add to the production bundle size.
I had removed this in #19745 in favor of the `act` exported by the
reconciler because I thought we would remove support for `act` in
production in the impending major release. (It currently warns.)
However, we've since decided to continue supporting `act` in prod for
now, so that it doesn't block people from upgrading to v17. We'll drop
support in a future major release.
So, to avoid bloating the production bundle size, we need to move the
public version of `act` back to "userspace", like it was before.
This doesn't negate the main goal of #19745, though, which was to
decouple the public version(s) of `act` from the internal one that we
use to test React itself.