Before this change, we would sometimes write segments without any content
in them. For example for a Suspense boundary that immediately suspends
we might emit something like:
<div hidden id="123">
<template id="456"></template>
</div>
Where the outer div is just a temporary wrapper and the inner one is a
placeholder for something to be added later.
This serves no purpose.
We should ideally have a heuristic that holds back segments based on byte
size and time. However, this is a straight forward clear win for now.
* Flight side of server context
* 1 more test
* rm unused function
* flow+prettier
* flow again =)
* duplicate ReactServerContext across packages
* store default value when lazily initializing server context
* .
* better comment
* derp... missing import
* rm optional chaining
* missed feature flag
* React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED ??
* add warning if non ServerContext passed into useServerContext
* pass context in as array of arrays
* make importServerContext nott pollute the global context state
* merge main
* remove useServerContext
* dont rely on object getters in ReactServerContext and disallow JSX
* add symbols to devtools + rename globalServerContextRegistry to just ContextRegistry
* gate test case as experimental
* feedback
* remove unions
* Lint
* fix oopsies (tests/lint/mismatching arguments/signatures
* lint again
* replace-fork
* remove extraneous change
* rebase
* 1 more test
* rm unused function
* flow+prettier
* flow again =)
* duplicate ReactServerContext across packages
* store default value when lazily initializing server context
* .
* better comment
* derp... missing import
* rm optional chaining
* missed feature flag
* React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED ??
* add warning if non ServerContext passed into useServerContext
* pass context in as array of arrays
* make importServerContext nott pollute the global context state
* merge main
* remove useServerContext
* dont rely on object getters in ReactServerContext and disallow JSX
* add symbols to devtools + rename globalServerContextRegistry to just ContextRegistry
* gate test case as experimental
* feedback
* remove unions
* Lint
* fix oopsies (tests/lint/mismatching arguments/signatures
* lint again
* replace-fork
* remove extraneous change
* rebase
* reinline
* rebase
* add back changes lost due to rebase being hard
* emit chunk for provider
* remove case for React provider type
* update type for SomeChunk
* enable flag with experimental
* add missing types
* fix flow type
* missing type
* t: any
* revert extraneous type change
* better type
* better type
* feedback
* change import to type import
* test?
* test?
* remove react-dom
* remove react-native-renderer from react-server-native-relay/package.json
* gate change in FiberNewContext, getComponentNameFromType, use switch statement in FlightServer
* getComponentNameFromTpe: server context type gated and use displayName if available
* fallthrough
* lint....
* POP
* lint
* write chunks to a buffer with no re-use
chunks were previously enqueued to a ReadableStream as they were written. We now write them to a view over an ArrayBuffer
and enqueue them only when writing has completed or the buffer's size is exceeded. In addition this copy now ensures
we don't attempt to re-send buffers that have already been transferred.
* refactor writeChunk to be more defensive and efficient
We now defend against overflows using the next views length instead of the current one. this protects us against a future where we use byobRequest and we get longer initial views than we might create after overflowing the first time. Additionally we add in an optimization when we have completely filled up the currentView where we avoid creating subarrays of the chunk to write since it lands exactly on a view boundary. Finally we move the view creation to beginWriting to avoid a runtime check on each write and because we want to reset the view on each beginWriting call in case a throw elsewhere in the program leaves the currentView in an unfinished state
* add tests to exercise codepaths dealing with buffer overlows
* I forgot to call onFatalError
I can't figure out how to write a test for this because it only happens
when there is a bug in React itself which would then be fixed if we found
it.
We're also covered by the protection of ReadableStream which doesn't leak
other errors to us.
* Abort requests if the reader cancels
No need to continue computing at this point.
* Abort requests if node streams get destroyed
This is if the downstream cancels is for example.
* Rename Node APIs for Parity with allReady
The "Complete" terminology is a little misleading because not everything
has been written yet. It's just "Ready" to be written now.
onShellReady
onShellError
onAllReady
* 'close' should be enough
* Move onCompleteAll to .allReady Promise
The onCompleteAll callback can sometimes resolve before the promise that
returns the stream which is tough to coordinate. A more idiomatic API
for a one shot event is a Promise.
That way the way you render for SEO or SSG is:
const stream = await renderToReadableStream(...);
await stream.readyAll;
respondWith(stream);
Ideally this should be a sub-class of ReadableStream but we don't yet
compile these to ES6 and they'd had to be to native class to subclass
a native stream.
I have other ideas for overriding the .tee() method in a subclass anyway.
So this is inline with that strategy.
* Reject the Promise on fatal errors
* Implement addEventListener and removeEventListener on Fabric HostComponent
* add files
* re-add CustomEvent
* fix flow
* Need to get CustomEvent from an import since it won't exist on the global scope by default
* yarn prettier-all
* use a mangled name consistently to refer to imperatively registered event handlers
* yarn prettier-all
* fuzzy null check
* fix capture phase event listener logic
* early exit from getEventListeners more often
* make some optimizations to getEventListeners and the bridge plugin
* fix accumulateInto logic
* fix accumulateInto
* Simplifying getListeners at the expense of perf for the non-hot path
* feedback
* fix impl of getListeners to correctly remove function
* pass all args in to event listeners
This information can help with bug investigation for renderers (like React Native) that embed the DevTools backend into their source (separately from the DevTools frontend, which gets run by the user).
If the DevTools backend is too old to report a version, or if the version reported is the same as the frontend (as will be the case with the browser extension) then only a single version string will be shown, as before. If a different version is reported, then both will be shown separately.
The src property on the dom element will return a fully qualified name and this does not match the dom
src attribute or the props provided to react. instead of reading from the element and re-assigning the property we
assign the property from props which is how it was initially assigned during the render
Co-authored-by: Josh Story <story@hey.com>
This PR refactors the cache code by moving it out of ReactFiberCacheComponent to ReactFiberTransitionPool in anticipation of it being reused by multiple stacks (ie. transition tracing)
* Move createRoot/hydrateRoot to /client
We want these APIs ideally to be imported separately from things you
might use in arbitrary components (like flushSync). Those other methods
are "isomorphic" to how the ReactDOM tree is rendered. Similar to hooks.
E.g. importing flushSync into a component that only uses it on the client
should ideally not also pull in the entry client implementation on the
server.
This also creates a nicer parity with /server where the roots are in a
separate entry point.
Unfortunately, I can't quite do this yet because we have some legacy APIs
that we plan on removing (like findDOMNode) and we also haven't implemented
flushSync using a flag like startTransition does yet.
Another problem is that we currently encourage these APIs to be aliased by
/profiling (or unstable_testing). In the future you don't have to alias
them because you can just change your roots to just import those APIs and
they'll still work with the isomorphic forms. Although we might also just
use export conditions for them.
For that all to work, I went with a different strategy for now where the
real API is in / but it comes with a warning if you use it. If you instead
import /client it disables the warning in a wrapper. That means that if you
alias / then import /client that will inturn import the alias and it'll
just work.
In a future breaking changes (likely when we switch to ESM) we can just
remove createRoot/hydrateRoot from / and move away from the aliasing
strategy.
* Update tests to import from react-dom/client
* Fix fixtures
* Update warnings
* Add test for the warning
* Update devtools
* Change order of react-dom, react-dom/client alias
I think the order matters here. The first one takes precedence.
* Require react-dom through client so it can be aliased
Co-authored-by: Andrew Clark <git@andrewclark.io>
We used to check for stream.locked in pull to see if we've been passed to
something that reads yet.
This was a bad hack because it won't actually call pull again if that changes.
The source of this is because the default for "highWaterMark" is 1 on some
streams. So it always wants to add one "chunk" (of size 1).
If we leave our high water mark as 0, we won't fill up any buffers unless we're
asked for more.
This web API is somewhat odd because it would be way more efficient if it
just told us how much the recipient wants instead of calling us once per
chunk.
Anyway, I turns out that if we define ourselves as a "bytes" type of
stream, the default also happens to be a high water mark of 0 so we can
just use that instead.
early load events will be missed by onLoad handlers if they trigger before the tree is committed
to avoid this we reset the src property on the img element to cause the browser to re-load
the img.
Co-authored-by: Josh Story <story@hey.com>
This brings the exports on npm to parity which simplifies things a bit.
We also don't plan to release this. It is used by Draft.js but that caller
will need to switch to flushSync.
There are a few internal tests that still need to be updated, so I'm
adding this flag back for www only.
The desired behavior rolled out to 10% public, so we're confident there
are no issues.
The open source behavior remains (skipUnmountedBoundaries = true).
* Failing test for react#23331
* Don't warn on hydration mismatch if suspended
When something suspends during hydration, we continue rendering the
siblings to warm up the cache and fire off any lazy network requests.
However, if there are any mismatches while rendering the siblings, it's
likely a false positive caused by the earlier suspended component. So
we should suppress any hydration warnings until the tree no
longer suspends.
Fixes#23332
Co-authored-by: Marcel Laverdet <marcel@laverdet.com>
No id should be a subset of any other id. Currently, this is not true
when there are multiple hooks in the same component. We append the
hook index to the end of the id, except for the first one. So you get
this pattern.
Before this change:
- 1st hook's id: :R0:
- 2nd hook's id: :R0:1:
The first hook's id is a subset of all the other ids in the
same component.
The fix for this is to use a different character to separate the main
id from the hook index. I've chosen a captial 'H' for this because
capital letters are not part of the base 32 character set when encoding
with `toString(32)`.
After this change:
- 1st hook's id: :R0:
- 2nd hook's id: :R0H1:
* Deprecate renderToNodeStream
* Use renderToPipeableStream in tests instead of renderToNodeStream
This is the equivalent API. This means that we have way less test coverage
of this API but I feel like that's fine since it has a deprecation warning
in it and we have coverage on renderToString that is mostly the same.
* Fix textarea bug
The test changes revealed a bug with textarea. It happens because we
currently always insert trailing comment nodes. We should optimize that
away. However, we also don't really support complex children so we
should toString it anyway which is what partial renderer used to do.
* Update tests that assert number of nodes
These tests are unnecessarily specific about number of nodes.
I special case these, which these tests already do, because they're good
tests to test that the optimization actually works later when we do
fix it.
The ids generated by useId are unique per React root. You can create
additional ids by concatenating them with locally unique strings.
To support this pattern, no id will ever be a subset of another id. We
achieve this by adding a special character to the beginning and end.
We use a colon (":") because it's uncommon — even if you don't prefix
the ids using the `identifierPrefix` option, collisions are unlikely.
One downside of a colon is that it's not a valid character in DOM
selectors, like `querySelectorAll`. We think this is probably
fine because it's not a common use case in React, and there are
workarounds or alternative solutions. But we're open to reconsidering
this in the future if there's a compelling argument.
* add transition name to startTransition
Add a transitionName to start transition, store the transition start time and name in the batch config, and pass it to the root on render
* Transition Tracing Types and Consts
* Root begin work
The root operates as a tracing marker that has all transitions on it. This PR only tested the root with one transition so far
- Store transitions in memoizedState. Do this in updateHostRoot AND attemptEarlyBailoutIfNoScheduledUpdate. We need to do this in the latter part because even if the root itself doesn't have an update, it could still have new transitions in its transitionLanes map that we need to process.
* Transition Tracing commit phase
- adds a module scoped pending transition callbacks object that contains all transition callbacks that have not yet been processed. This contains all callbacks before the next paint occurs.
- Add code in the mutation phase to:
* For the root, if there are transitions that were initialized during this commit in the root transition lanes map, add a transition start call to the pending transition callbacks object. Then, remove the transitions from the root transition lanes map.
* For roots, in the commit phase, add a transition complete call
We add this code in the mutation phase because we can't add it to the passive phase because then the paint might have occurred before we even know which callbacks to call
* Process Callbacks after paint
At the end of the commit phase, call scheduleTransitionCallbacks to schedule all pending transition callbacks to be called after paint. Then clear the callbacks
For createRoot, a common mistake is to pass JSX as the second argument,
instead of calling root.render.
For hydrateRoot, a common mistake is to forget to pass children as
the second argument.
The type system will enforce correct usage, but since not everyone uses
types we'll log a helpful warning, too.
* Refactor warnForTextDifference
We're going to fork the behavior of this function between concurrent
roots and legacy roots.
The legacy behavior is to warn in dev when the text mismatches during
hydration. In concurrent roots, we'll log a recoverable error and revert
to client rendering. That means this is no longer a development-only
function — it affects the prod behavior, too.
I haven't changed any behavior in this commit. I only rearranged the
code slightly so that the dev environment check is inside the body
instead of around the function call. I also threaded through an
isConcurrentMode argument.
* Revert to client render on text content mismatch
Expands the behavior of enableClientRenderFallbackOnHydrationMismatch to
check text content, too.
If the text is different from what was rendered on the server, we will
recover the UI by falling back to client rendering, up to the nearest
Suspense boundary.
* Remove object-assign polyfill
We really rely on a more modern environment where this is typically
polyfilled anyway and we don't officially support IE with more extensive
polyfilling anyway. So all environments should have the native version
by now.
* Use shared/assign instead of Object.assign in code
This is so that we have one cached local instance in the bundle.
Ideally we should have a compile do this for us but we already follow
this pattern with hasOwnProperty, isArray, Object.is etc.
* Transform Object.assign to now use shared/assign
We need this to use the shared instance when Object.spread is used.
@sebmarkbage and I audited the feature flags file to review the status
of each feature or experiment. Based on that, I've added some more
comments to the main ReactFeatureFlags module and rearranged them
into groups.
I haven't changed the value of any flags, yet. There are a few we're
going to land but I'll do them as separate PRs.
I removed useMutableSource in a previous PR but forgot this one.
We still export it in the FB builds until we can migrate the internal
callers (Recoil).
This was already defeating the XSS issue that Symbols was meant to protect
against. So you were already supposed to use a polyfill for security.
We rely on real Symbol.for in Flight for Server Components so those require
real symbols anyway.
We also don't really support IE without additional polyfills anyway.
This function was modeled after Node streams where write returns a boolean
whether to keep writing or not. I think we should probably switch this
up and read desired size explicitly in appropriate places.
However, in the meantime, we don't have to return a value where we're
not going to use it. So I split this so that we call writeChunkAndReturn
if we're going to return the boolean.
This should help with the compilation so that they can be inlined.
* tests: add failing test to demonstrate bug in ReadableStream implementation
* Re-add reentrancy avoidance
I removed the equivalency of this in #22446. However, I didn't fully
understand the intended semantics of the spec but I understand this better
now.
The spec is not actually recursive. It won't call pull again inside of a
pull. It might not call it inside startWork neither which the original
issue avoided. However, it will call pull if you enqueue to the controller
without filling up the desired size outside any call.
We could avoid that by returning a Promise from pull that we wait to
resolve until we've performed all our pending tasks. That would be the
more idiomatic solution. That's a bit more involved but since we know
understand it, we can readd the reentrancy hack since we have an easy place
to detect it. If anything, it should probably throw or log here otherwise.
I believe this fixes#22772.
This includes the test from #22889 but should ideally have one for Fizz.
Co-authored-by: Josh Larson <josh.larson@shopify.com>
We only export the source directory so Jest and Rollup can access them
during local development and at build time. The files don't exist in the
public builds, so we don't need the export entry, either.
The unstable-shared-subset.js file is not a public module — it's a
private module that the "react" package maps to when it's accessed from
the "react-server" package.
We originally added it because it was required to make our Rollup
configuration work, because at the time only "public" modules could act
as the entry point for a build artifact — that's why it's prefixed with
"unstable". We've since updated our Rollup config to support private
entry points, so we can remove the extra indirection.
* fix getSnapshot warning when a selector returns NaN
useSyncExternalStoreWithSelector delegate a selector as
getSnapshot of useSyncExternalStore.
* Fiber's use sync external store has a same issue
* Small nits
We use Object.is to check whether the snapshot value has been updated,
so we should also use it to check whether the value is cached.
Co-authored-by: Andrew Clark <git@andrewclark.io>
We need to use the Offscreen Fiber's update queue for interaction tracing. This PR removes the optimization that React Cache uses to not need to push and pop the cache in special circumstances and defaults to always pushing and popping the cache as long as there was a previous cache.