Adds a deprecation warning to ReactDOMTestUtils.renderIntoDocument,
which is removed in version 19.
Also backports the deprecation warning for ReactDOMTestUtils.act.
Previously, the `refs` property of a class component instance was
read-only by user code — only React could write to it, and until/unless
a string ref was used, it pointed to a shared empty object that was
frozen in dev to prevent userspace mutations.
Because string refs are deprecated, we want users to be able to codemod
all their string refs to callback refs. The safest way to do this is to
output a callback ref that assigns to `this.refs`.
So to support this, we need to make `this.refs` writable by userspace.
This commit adds warnings indicating that `renderToStaticNodeStream`
will be removed in an upcoming React release. This API has been legacy,
is not widely used (renderToStaticMarkup is more common) and has
semantically eqiuvalent implementations with renderToReadableStream and
renderToPipeableStream.
landed in main in #28872
changed the warning to match renderToNodeStream
This backports a deprecation warning for legacy context, even when
Strict Mode is not enabled.
I didn't bother to update all the tests because the tests are in such
a different state than what's on `main`, and on `main` we already
updated the tests accordingly. So instead I silenced the warnings in
our test config, like we've done for other warnings in the past.
This improves the error message a bit and ensures that we recommend
putting the key first, not last, which ensures that the faster
`jsx-runtime` is used.
This only affects the modern "automatic" JSX transform.
We're going to use this branch to release a minor 18.3 release based off
the published 18.2 release revision. This will include some additional
warnings to assist in upgrading to React 19, but no behavior changes
compared to 18.2.
I bumped the React version to 18.3 and all the other packages by a patch
revision (since we're not going to update anything in those).
The eslint-disable-next-line opt out for prod error minification was not properly working. In the build a replacable error was output even though it was not failing the build. This change refactors the code to avoid the erroneous behavior but a fix for the lint may be better
* [Fizz] Support abort reasons
Fizz supports aborting the render but does not currently accept a reason. The various render functions that use Fizz have some automatic and some user-controlled abort semantics that can be useful to communicate with the running program and users about why an Abort happened.
This change implements abort reasons for renderToReadableStream and renderToPipeable stream as well as legacy renderers such as renderToString and related implementations.
For AbortController implementations the reason passed to the abort method is forwarded to Fizz and sent to the onError handler. If no reason is provided the AbortController should construct an AbortError DOMException and as a fallback Fizz will generate a similar error in the absence of a reason
For pipeable streams, an abort function is returned alongside pipe which already accepted a reason. That reason is now forwarded to Fizz and the implementation described above.
For legacy renderers there is no exposed abort functionality but it is used internally and the reasons provided give useful context to, for instance to the fact that Suspense is not supported in renderToString-like renderers
* Add `isHidden` to OffscreenInstance
We need to be able to read whether an offscreen tree is hidden from
an imperative event. We can store this on its OffscreenInstance.
We were already scheduling a commit effect whenever the visibility
changes, in order to toggle the inner effects. So we can reuse that.
* [FORKED] Bugfix: Revealing a hidden update
This fixes a bug I discovered related to revealing a hidden Offscreen
tree. When this happens, we include in that render all the updates that
had previously been deferred — that is, all the updates that would have
already committed if the tree weren't hidden. This is necessary to avoid
tearing with the surrounding contents. (This was the "flickering"
Suspense bug we found a few years ago: #18411.)
The way we do this is by tracking the lanes of the updates that were
deferred by a hidden tree. These are the "base" lanes. Then, in order
to reveal the hidden tree, we process any update that matches one of
those base lanes.
The bug I discovered is that some of these base lanes may include
updates that were not present at the time the tree was hidden. We cannot
flush those updates earlier that the surrounding contents — that, too,
could cause tearing.
The crux of the problem is that we sometimes reuse the same lane for
base updates and for non-base updates. So the lane alone isn't
sufficient to distinguish between these cases. We must track this in
some other way.
The solution I landed upon was to add an extra OffscreenLane bit to any
update that is made to a hidden tree. Then later when we reveal the
tree, we'll know not to treat them as base updates.
The extra OffscreenLane bit is removed as soon as that lane is committed
by the root (markRootFinished) — at that point, it gets
"upgraded" to a base update.
The trickiest part of this algorithm is reliably detecting when an
update is made to a hidden tree. What makes this challenging is when the
update is received during a concurrent event, while a render is already
in progress — it's possible the work-in-progress render is about to
flip the visibility of the tree that's being updated, leading to a race
condition.
To avoid a race condition, we will wait to read the visibility of the
tree until the current render has finished. In other words, this makes
it an atomic operation. Most of this logic was already implemented
in #24663.
Because this bugfix depends on a moderately risky refactor to the update
queue (#24663), it only works in the "new" reconciler fork. We will roll
it out gradually to www before landing in the main fork.
* Add previous commit to list of forked revisions
We don't need to run DevTools regression tests once an hour, and also it makes getting the most recent react build or react devtools build really annoying, so run them once a day instead
* [Fizz] Disallow complex children in <title> elements
<title> Elements in the DOM can only have Text content. In Fizz if more than one text node is emitted an HTML comment node is used as a text separator. Unfortunately because of the content restriction of the DOM representation of the title element this separator is displayed as escaped text which is not what the component author intended.
This commit special cases title handling, primarily to issue warnings if you pass complex children to <title>. At the moment title expects to receive a single child or an array of length 1. In both cases the type of that child must be string or number. If anything more complex is provided a warning will be logged to the console explaining why this is problematic.
There is no runtime behavior change so broken things are still broken (e.g. returning two text nodes which will cause a separator or using Suspense inside title children) but they should at least be accompanied by warnings that are useful.
One edge case that will now warn but won't technically break an application is if you use a Component that returns a single string as a child of title. This is a form of indirection that works but becasue we cannot discriminate between a Component that will follow the rules and one that violates them the warning is issued regardless.
* fixup dev warning conditional logic
* lints
* fix bugs
* extend onRecoverableError API to support errorInfo
errorInfo has been used in Error Boundaries wiht componentDidCatch for a while now. To date this metadata only contained a componentStack. onRecoverableError only receives an error (type mixed) argument and thus providing additional error metadata was not possible without mutating user created mixed objects.
This change modifies rootConcurrentErrors rootRecoverableErrors, and hydrationErrors so all expect CapturedValue types. additionally a new factory function allows the creation of CapturedValues from a value plus a hash and stack.
In general, client derived CapturedValues will be created using the original function which derives a componentStack from a fiber and server originated CapturedValues will be created using with a passed in hash and optional componentStack.
We have a currently unreproducible flaky e2e test. This PR captures snapshots on e2e test failures so we can better debug flaky e2e tests that don't fail locally.
* Always push updates to interleaved queue first
Interleaves updates (updates that are scheduled while another render
is already is progress) go into a special queue that isn't applied until
the end of the current render. They are transferred to the "real" queue
at the beginning of the next render.
Currently we check during `setState` whether an update should go
directly onto the real queue or onto the special interleaved queue. The
logic is subtle and it can lead to bugs if you mess it up, as in #24400.
Instead, this changes it to always go onto the interleaved queue. The
behavior is the same but the logic is simpler.
As a further step, we can also wait to update the `childLanes` until
the end of the current render. I'll do this in the next step.
* Move setState return path traversal to own call
A lot of the logic around scheduling an update needs access to the
fiber root. To obtain this reference, we must walk up the fiber return
path. We also do this to update `childLanes` on all the parent
nodes, so we can use the same traversal for both purposes.
The traversal currently happens inside `scheduleUpdateOnFiber`, but
sometimes we need to access it beyond that function, too.
So I've hoisted the traversal out of `scheduleUpdateOnFiber` into its
own function call that happens at the beginning of the
`setState` algorithm.
* Rename ReactInterleavedUpdates -> ReactFiberConcurrentUpdates
The scope of this module is expanding so I've renamed accordingly. No
behavioral changes.
* Enqueue and update childLanes in same function
During a setState, the childLanes are updated immediately, even if a
render is already in progress. This can lead to subtle concurrency bugs,
so the plan is to wait until the in-progress render has finished before
updating the childLanes, to prevent subtle concurrency bugs.
As a step toward that change, when scheduling an update, we should not
update the childLanes directly, but instead defer to the
ReactConcurrentUpdates module to do it at the appropriate time.
This makes markUpdateLaneFromFiberToRoot a private function that is
only called from the ReactConcurrentUpdates module.
* [FORKED] Don't update childLanes until after current render
(This is the riskiest commit in the stack. Only affects the "new"
reconciler fork.)
Updates that occur in a concurrent event while a render is already in
progress can't be processed during that render. This is tricky to get
right. Previously we solved this by adding concurrent updates to a
special `interleaved` queue, then transferring the `interleaved` queue
to the `pending` queue after the render phase had completed.
However, we would still mutate the `childLanes` along the parent path
immediately, which can lead to its own subtle data races.
Instead, we can queue the entire operation until after the render phase
has completed. This replaces the need for an `interleaved` field on
every fiber/hook queue.
The main motivation for this change, aside from simplifying the logic a
bit, is so we can read information about the current fiber while we're
walking up its return path, like whether it's inside a hidden tree.
(I haven't done anything like that in this commit, though.)
* Add 17691ac to forked revisions
* Track revs that intentionaly fork the reconciler
When we fork the the "old" and "new" reconciler implementations, it can
be difficult to keep track of which commits introduced the delta
in behavior. This makes bisecting difficult if one of the changes
introduces a bug.
I've added a new file called `forked-revisions` that contains the list
of commits that intentionally forked the reconcilers.
In CI, we'll confirm that the reconcilers are identical except for the
changes in the listed revisions. This also ensures that the revisions
can be cleanly reverted.
* [TEST] Add trivial divergence between forks
This should fail CI. We'll see if the next commit fixes it.
* [TEST] Update list of forked revisions
This should fix CI
* Revert temporary fork
This reverts the temporary fork added in the previous commits that was
used to test CI.
* Update error message when CI fails
The download_build job needs to persist its artifacts to the workspace
so downstream jobs can access them.
Persist the same directories as the normal build job.
Fixes a mistake in #24676. The get_base_build job downloads artifacts to
`base-build` instead of `build`, so that sizebot can compare the two
directories. For most other jobs, though, we want it to produce the
same output as the normal build job.
When running the hourly DevTools testing workflow, we don't need to
build React from scratch each time; we can download its build artifacts,
like we do for sizebot and the release workflow.
* Allow aritfacts download even if CI is broken
Adds an option to the download script to disable the CI check and
continue downloading the artifacts even if CI is broken.
I often rely on this to debug broken build artifacts. I was thinking
the sizebot should also use this when downloading the base artifacts
from main, since for the purposes of size tracking, it really doesn't
matter whether the base commit is broken.
* Sizebot should work even if base rev is broken
Sizebot works by downloading the build artifacts for the base revision
and comparing the fize sizes, but the download script will fail if
the base revision has a failing CI job. This happens more often than it
should because of flaky cron jobs, but even when it does, we shouldn't
let it affect the sizebot — for the purposes of tracking sizes, it
doesn't really matter whether the base revision is broken.
Made a couple of fixes to the `devtools-test-shell`
* test selectors aren't available in > React v18.0 either, so we'll need to mock the test selector functions there as well
* `react-dom/client` should map to `react-dom/client` and not `react-dom`
* Implements useId hook for Flight server.
The approach for ids for Flight is different from Fizz/Client where there is a need for determinancy. Flight rendered elements will not be rendered on the client and as such the ids generated in a request only need to be unique. However since FLight does support refetching subtrees it is possible a client will need to patch up a part of the tree rather than replacing the entire thing so it is not safe to use a simple incrementing counter. To solve for this we allow the caller to specify a prefix. On an initial fetch it is likely this will be empty but on refetches or subtrees we expect to have a client `useId` provide the prefix since it will guaranteed be unique for that subtree and thus for the entire tree. It is also possible that we will automatically provide prefixes based on a client/Fizz useId on refetches
in addition to the core change I also modified the structure of options for renderToReadableStream where `onError`, `context`, and the new `identifierPrefix` are properties of an Options object argument to avoid the clumsiness of a growing list of optional function arguments.
* defend against useId call outside of rendering
* switch to S from F for Server Component ids
* default to empty string identifier prefix
* Add a test demonstrating that there is no warning when double rendering on the client a server component that used useId
* lints and gates
* Explicitly set highWaterMark to 0 for ReadableStreams
This is because not all streaming implementations respect the
default behavior of settings highWaterMark to 0 for byte streams.
Being explicit guarantees the intended behavior across runtimes.
* Remove size methods and add FlowFixMe instead
Modified the `run_devtools_e2e_tests` script so that you can pass in a React version. If you pass in a version, it will build the DevTools shell and run the e2e tests with that version.
This PR adds a `--replaceBuild` option to the script that downloads older React version builds. If this flag is true, we will replace the contents of the `build` folder with the contents of the `build-regression` folder and remove the `build-regression` folder after, which was the original behavior.
However, for e2e tests, we need both the original build (for DevTools) and the new build (for the React Apps), so we need both the `build` and the `build-regression` folders. Not adding the `--replaceBuild` option will do this.
This PR also modifies the circle CI config to reflect this change.
* use return from onError
* export getSuspenseInstanceFallbackError
* stringToChunk
* return string from onError in downstream type signatures
* 1 more type
* support encoding errors in html stream and escape user input
This commit adds another way to get errors to the suspense instance by encoding them as dataset properties of a template element at the head of the boundary. Previously if there was an error before the boundary flushed there was no way to stream the error to the client because there would never be a client render instruction.
Additionally the error is sent in 3 parts
1) error hash - this is always sent (dev or prod) if one is provided
2) error message - Dev only
3) error component stack - Dev only, this now captures the stack at the point of error
Another item addressed in this commit is the escaping of potentially unsafe data. all error components are escaped as test for browers when written into the html and as javascript strings when written into a client render instruction.
* nits
Co-authored-by: Marco Salazar <salazarm@fb.com>
* [Fizz] Improve text separator byte efficiency
Previously text separators were inserted following any Text node in Fizz. This increases bytes sent when streaming and in some cases such as title elements these separators are not interpreted as comment nodes and leak into the visual aspects of a page as escaped text.
The reason simple tracking on the last pushed type doesn't work is that Segments can be filled in asynchronously later and so you cannot know in a single pass whether the preceding content was a text node or not. This commit adds a concept of TextEmbedding which provides a best effort signal to Segments on whether they are embedded within text. This allows the later resolution of that Segment to add text separators when possibly necessary but avoid them when they are surely not.
The current implementation can only "peek" head if the segment is a the Root Segment or a Suspense Boundary Segment. In these cases we know there is no trailing text embedding and we can eliminate the separator at the end of the segment if the last emitted element was Text. In normal Segments we cannot peek and thus have to assume there might be a trailing text embedding and we issue a separator defensively. This should be rare in practice as it is assumed most components that will cause segment creation will also emit some markup at the edges.
* [Fizz] Improve separator efficiency when flushing delayed segments
The method by which we get segment markup into the DOM differs depending on when the Segment resolves.
If a Segment resolves before flushing begins for it's parent it will be emitted inline with the parent markup. In these cases separators may be necessary because they are how we clue the browser into breakup up text into distinct nodes that will later match up with what will be hydrated on the client.
If a Segment resolves after flushing has happened a script will be used to patch up the DOM in the client. when this happens if there are any text nodes on the boundary of the patch they won't be "merged" and thus will continue to have distinct representation as Nodes in the DOM. Thus we can avoid doing any separators at the boundaries in these cases.
After applying these changes the only time you will get text separators as follows
* in between serial text nodes that emit at the same time - these are necessary and cannot be eliminated unless we stop relying on the browser to automatically parse the correct text nodes when processing this HTML
* after a final text node in a non-boundary segment that resolves before it's parent has flushed - these are sometimes extraneous, like when the next emitted thing is a non-Text node.
In all other cases text separators should be omitted which means the general byte efficiency of this approach should be pretty good
On the server we have a similar translation map from the file path that the
loader uses to the refer to the original module and to the bundled module ID.
The Flight server is optimized to emit the smallest format for the client.
However during SSR, the same client component might go by a different
module ID since it's a different bundle than the client bundle.
This provides an option to add a translation map from client ID to SSR ID
when reading the Flight stream.
Ideally we should have a special SSR Flight Client that takes this option
but for now we only have one Client for both.
This PR adds an e2e regression app to the react-devtools-shell package. This app:
* Has an app.js and an appLegacy.js entrypoint because apps prior to React 18 need to use ReactDOM.render. These files will create and render multiple test apps (though they currently only render the List)
* Moved the ListApp out of the e2e folder and into an e2e-apps folder so that both e2e and e2e-regression can use the same test apps
* Creates a ListAppLegacy app because prior to React 16.8 hooks didn't exist.
* Added a devtools file for the e2e-regression
* Modifies the webpack config so that the e2e-regression React app can use different a different React version than DevTools
This PR:
* Increases test retry count to 2 so that flaky tests have more of a chance to pass
* Ideally most e2e tests will run for all React versions (and ensure DevTools elegantly fails if React doesn't support its features). However, some features aren't supported in older React versions at all (ex. Profiling) Add runOnlyForReactRange function in these cases to skip tests that don't satisfy the correct React semver range
* Fix should allow searching for component by name test, which was flaky because sometimes the Searchbox would be unfocused the second time we try to type in it
* Edited test Should allow elements to be inspected to check that element inspect gracefully fails in older React versions
* Updated config to add a config.use.url field and a config.use.react_version field, which change depending on the React Version (and whether it's specified)
* Move hydration code out of normal Suspense path
Shuffling some code around to make it easier to follow. The logic for
updating a dehydrated Suspense boundary is significantly different
from the logic for a client-rendered Suspense boundary. Most of it was
already lifted out into a separate function; this moves the remaining
hydration-specific logic out of updateSuspenseComponent and into
updateDehydratedSuspenseComponent instead.
No expected changes to program behavior.
* Extract hydration logic in complete phase, too
Same as previous step but for the complete phase. This is a separate
commit to make bisecting easier in case something breaks. The logic
is very subtle but mostly all I've done is extract it to
another function.
This PR adds an hourly chron job on Circle CI that runs regression tests on the most recent DevTools build for React v16.0, v16.5, v16.8 v17.0 and v18.0.