Commit Graph
470 Commits
Author SHA1 Message Date
Andrew Clark 2e0d86d221 Allow updating dehydrated root at lower priority without forcing client render (#24082)
* Pass children to hydration root constructor

I already made this change for the concurrent root API in #23309. This
does the same thing for the legacy API.

Doesn't change any behavior, but I will use this in the next steps.

* Add isRootDehydrated function

Currently this does nothing except read a boolean field, but I'm about
to change this logic.

Since this is accessed by React DOM, too, I put the function in a
separate module that can be deep imported. Previously, it was accessing
the FiberRoot directly. The reason it's a separate module is to break a
circular dependency between React DOM and the reconciler.

* Allow updates at lower pri without forcing client render

Currently, if a root is updated before the shell has finished hydrating
(for example, due to a top-level navigation), we immediately revert to
client rendering. This is rare because the root is expected is finish
quickly, but not exceedingly rare because the root may be suspended.

This adds support for updating the root without forcing a client render
as long as the update has lower priority than the initial hydration,
i.e. if the update is wrapped in startTransition.

To implement this, I had to do some refactoring. The main idea here is
to make it closer to how we implement hydration in Suspense boundaries:

- I moved isDehydrated from the shared FiberRoot object to the
HostRoot's state object.
- In the begin phase, I check if the root has received an by comparing
the new children to the initial children. If they are different, we
revert to client rendering, and set isDehydrated to false using a
derived state update (a la getDerivedStateFromProps).
- There are a few places where we used to set root.isDehydrated to false
as a way to force a client render. Instead, I set the ForceClientRender
flag on the root work-in-progress fiber.
- Whenever we fall back to client rendering, I log a recoverable error.

The overall code structure is almost identical to the corresponding
logic for Suspense components.

The reason this works is because if the update has lower priority than
the initial hydration, it won't be processed during the hydration
render, so the children will be the same.

We can go even further and allow updates at _higher_ priority (though
not sync) by implementing selective hydration at the root, like we do
for Suspense boundaries: interrupt the current render, attempt hydration
at slightly higher priority than the update, then continue rendering the
update. I haven't implemented this yet, but I've structured the code in
anticipation of adding this later.

* Wrap useMutableSource logic in feature flag
2022-03-20 16:18:51 -04:00
Sebastian Markbåge 1c44437355 Fix createRoot container signature (#24110)
The internal Container type represents the types of containers that React
can support in its internals that deal with containers.

This didn't include DocumentFragment which we support specifically for
rendering into shadow roots.

However, not all types makes sense to pass into the createRoot API.

One of those is comment nodes that is deprecated and we don't really fully
support. It really only exists for FB legacy.

For createRoot it doesn't make sense to pass a Document since that will try
to empty the document which removes the HTML tag which doesn't work.
Documents can only be passed to hydrateRoot.

Conversely I'm not sure we actually support hydrating a shadow root properly
so I excluded DocumentFragment from hydrateRoot.
2022-03-16 20:06:00 -04:00
salazarm ef23a9ee81 Flag for text hydration mismatch (#24107)
* flag for text hydration mismatch

* rm unused import
2022-03-16 10:12:59 -04:00
Andrew Clark 832e2987e0 Revert accdientally merged PR (#24081) 2022-03-11 21:31:23 -05:00
Andrew Clark c8e4789e21 Pass children to hydration root constructor
I already made this change for the concurrent root API in #23309. This
does the same thing for the legacy API.

Doesn't change any behavior, but I will use this in the next steps.
2022-03-11 20:44:25 -05:00
Josh StoryandJosh Story f0dd459e0d reassign src from props not the dom element src property (#23395)
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>
2022-03-01 18:23:16 -05:00
Sebastian MarkbågeandAndrew Clark 17806594cc Move createRoot/hydrateRoot to react-dom/client (#23385)
* 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>
2022-03-01 00:13:28 -05:00
Josh StoryandJosh Story 086fa8ee2f re-trigger load events for img elements on commit (#23316)
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>
2022-02-28 18:07:39 -05:00
Andrew ClarkandMarcel Laverdet f468816ef1 Fix false positive hydration warnings (#23364)
* 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>
2022-02-25 18:10:10 -05:00
Sebastian Markbåge 68cb55f262 Add more warnings for second argument to root.render. (#23358)
We already had one for callbacks but containers is also an easy mistake.
2022-02-24 16:31:48 -05:00
Andrew Clark 8c4cd65cfa Add warnings for common root API mistakes (#23356)
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.
2022-02-24 10:57:37 -05:00
Andrew Clark a5b22155c8 Warn if renderSubtreeIntoContainer is called (#23355)
We already warn for all the other legacy APIs. Forgot to enable
this one.
2022-02-24 01:42:22 -05:00
Andrew Clark 52c393b5d2 Revert to client render on text mismatch (#23354)
* 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.
2022-02-24 00:23:56 -05:00
Sebastian Markbåge 1ad8d81292 Remove object-assign polyfill (#23351)
* 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.
2022-02-23 19:34:24 -05:00
Andrew Clark 54f785bc51 Disallow comments as DOM containers for createRoot (#23321)
This is an old feature that we no longer support. `hydrateRoot` already
throws if you pass a comment node; this change makes `createRoot`
throw, too.

Still enabled in the Facebook build until we migrate the callers.
2022-02-17 16:44:22 -05:00
Andrew Clark 80059bb730 Switch to client rendering if root receives update (#23309)
If a hydration root receives an update before the outermost shell has
finished hydrating, we should give up hydrating and switch to
client rendering.

Since the shell is expected to commit quickly, this doesn't happen that
often. The most common sequence is something in the shell suspends, and
then the user quickly navigates to a different screen, triggering a
top-level update.

Instead of immediately switching to client rendering, we could first
attempt to hydration at higher priority, like we do for updates that
occur inside nested dehydrated trees.

But since this case is expected to be rare, and mainly only happens when
the shell is suspended, an attempt at higher priority would likely end
up suspending again anyway, so it would be wasted effort. Implementing
it this way would also require us to add a new lane especially for root
hydration. For simplicity's sake, we'll immediately switch to client
rendering. In the future, if we find another use case for a root
hydration lane, we'll reconsider.
2022-02-16 13:15:25 -05:00
Luna Ruan 1fb0d06878 [Devtools][Transition Tracing] Add Transition callbacks to createRoot (#23276)
- Add the type of transition tracing callbacks
- Add transition tracing callbacks as an option to `createRoot`
- Add transition tracing callbacks on the root
- Add option to pass transition tracing callbacks to createReactNoop
2022-02-11 10:15:10 -08:00
Andrew Clark efd8f6442d Resolve default onRecoverableError at root init (#23264)
Minor follow up to initial onRecoverableError PR.

When onRecoverableError is not provided to `createRoot`, the
renderer falls back to a default implementation. Originally I
implemented this with a host config method, but what we can do instead
is pass the default implementation the root constructor as if it were
a user provided one.
2022-02-10 07:59:10 -08:00
Andrew Clark 848e802d20 Add onRecoverableError option to hydrateRoot, createRoot (#23207)
* [RFC] Add onHydrationError option to hydrateRoot

This is not the final API but I'm pushing it for discussion purposes.

When an error is thrown during hydration, we fallback to client
rendering, without triggering an error boundary. This is good because,
in many cases, the UI will recover and the user won't even notice that
something has gone wrong behind the scenes.

However, we shouldn't recover from these errors silently, because the
underlying cause might be pretty serious. Server-client mismatches are
not supposed to happen, even if UI doesn't break from the users
perspective. Ignoring them could lead to worse problems later. De-opting
from server to client rendering could also be a significant performance
regression, depending on the scope of the UI it affects.

So we need a way to log when hydration errors occur.

This adds a new option for `hydrateRoot` called `onHydrationError`. It's
symmetrical to the server renderer's `onError` option, and serves the
same purpose.

When no option is provided, the default behavior is to schedule a
browser task and rethrow the error. This will trigger the normal browser
behavior for errors, including dispatching an error event. If the app
already has error monitoring, this likely will just work as expected
without additional configuration.

However, we can also expose additional metadata about these errors, like
which Suspense boundaries were affected by the de-opt to client
rendering. (I have not exposed any metadata in this commit; API needs
more design work.)

There are other situations besides hydration where we recover from an
error without surfacing it to the user, or notifying an error boundary.
For example, if an error occurs during a concurrent render, it could be
due to a data race, so we try again synchronously in case that fixes it.
We should probably expose a way to log these types of errors, too. (Also
not implemented in this commit.)

* Log all recoverable errors

This expands the scope of onHydrationError to include all errors that
are not surfaced to the UI (an error boundary). In addition to errors
that occur during hydration, this also includes errors that recoverable
by de-opting to synchronous rendering. Typically (or really, by
definition) these errors are the result of a concurrent data race;
blocking the main thread fixes them by prevents subsequent races.

The logic for de-opting to synchronous rendering already existed. The
only thing that has changed is that we now log the errors instead of
silently proceeding.

The logging API has been renamed from onHydrationError
to onRecoverableError.

* Don't log recoverable errors until commit phase

If the render is interrupted and restarts, we don't want to log the
errors multiple times.

This change only affects errors that are recovered by de-opting to
synchronous rendering; we'll have to do something else for errors
during hydration, since they use a different recovery path.

* Only log hydration error if client render succeeds

Similar to previous step.

When an error occurs during hydration, we only want to log it if falling
back to client rendering _succeeds_. If client rendering fails,
the error will get reported to the nearest error boundary, so there's
no need for a duplicate log.

To implement this, I added a list of errors to the hydration context.
If the Suspense boundary successfully completes, they are added to
the main recoverable errors queue (the one I added in the
previous step.)

* Log error with queueMicrotask instead of Scheduler

If onRecoverableError is not provided, we default to rethrowing the
error in a separate task. Originally, I scheduled the task with
idle priority, but @sebmarkbage made the good point that if there are
multiple errors logs, we want to preserve the original order. So I've
switched it to a microtask. The priority can be lowered in userspace
by scheduling an additional task inside onRecoverableError.

* Only use host config method for default behavior

Redefines the contract of the host config's logRecoverableError method
to be a default implementation for onRecoverableError if a user-provided
one is not provided when the root is created.

* Log with reportError instead of rethrowing

In modern browsers, reportError will dispatch an error event, emulating
an uncaught JavaScript error. We can do this instead of rethrowing
recoverable errors in a microtask, which is nice because it avoids any
subtle ordering issues.

In older browsers and test environments, we'll fall back
to console.error.

* Naming nits

queueRecoverableHydrationErrors -> upgradeHydrationErrorsToRecoverable
2022-02-04 07:57:33 -08:00
Joey Arhar a87adefecd Allow functions to be passed to custom element setters (#23042)
This is part of the new custom element features that were implemented
here:
https://github.com/facebook/react/commit/24dd07bd269590ee5024b7f0f1906887d256ea86

When a custom element has a setter for a property and passes the `in`
heuristic, the value passed to the react property should be assigned
directly to the custom element's property, regardless of the type of the
value. However, it was discovered that this isn't working with
functions. This patch makes it work with functions.

Fixes https://github.com/facebook/react/issues/23041
2022-01-12 20:12:07 +00:00
Borja Paz Rodríguez 42c30e8b12 Correct typing errors detected in several files (#22965)
* Correct spelling errors in several files
* Correct typos in devtools-shared package files
2022-01-04 10:30:07 -05:00
Joey Arhar 24dd07bd26 Add custom element property support behind a flag (#22184)
* custom element props

* custom element events

* use function type for on*

* tests, htmlFor

* className

* fix ReactDOMComponent-test

* started on adding feature flag

* added feature flag to all feature flag files

* everything passes

* tried to fix getPropertyInfo

* used @gate and __experimental__

* remove flag gating for test which already passes

* fix onClick test

* add __EXPERIMENTAL__ to www flags, rename eventProxy

* Add innerText and textContent to reservedProps

* Emit warning when assigning to read only properties in client

* Revert "Emit warning when assigning to read only properties in client"

This reverts commit 1a093e584c.

* Emit warning when assigning to read only properties during hydration

* yarn prettier-all

* Gate hydration warning test on flag

* Fix gating in hydration warning test

* Fix assignment to boolean properties

* Replace _listeners with random suffix matching

* Improve gating for hydration warning test

* Add outerText and outerHTML to server warning properties

* remove nameLower logic

* fix capture event listener test

* Add coverage for changing custom event listeners

* yarn prettier-all

* yarn lint --fix

* replace getCustomElementEventHandlersFromNode with getFiberCurrentPropsFromNode

* Remove previous value when adding event listener

* flow, lint, prettier

* Add dispatchEvent to make sure nothing crashes

* Add state change to reserved attribute tests

* Add missing feature flag test gate

* Reimplement SSR changes in ReactDOMServerFormatConfig

* Test hydration for objects and functions

* add missing test gate

* remove extraneous comment

* Add attribute->property test
2021-12-08 15:11:42 +00:00
salazarm 5041c37d27 Remove hydrate option from createRoot (#22878)
* remove hydrate: true option

* remove missed comment

* lint

* warning

* circumvent flow
2021-12-07 16:10:00 -05:00
Andrew Clark 4729ff6d1f Implement identifierPrefix option for useId (#22855)
When an `identifierPrefix` option is given, React will add it to the
beginning of ids generated by `useId`.

The main use case is to avoid conflicts when there are multiple React
roots on a single page.

The server API already supported an `identifierPrefix` option. It's not
only used by `useId`, but also for React-generated ids that are used to
stitch together chunks of HTML, among other things. I added a
corresponding option to the client.

You must pass the same prefix option to both the server and client.
Eventually we may make this automatic by sending the prefix from the
server as part of the HTML stream.
2021-12-02 17:49:43 -08:00
salazarm 4ff5f5719b Move unstable_scheduleHydration to ReactDOMHydrationRoot (#22455)
* move unstable_scheduleHydration to ReactDOMHydrationRoot

* move definition of schedule hydration

* fix test?

* prototype

* fix test

* remove gating because unstable_scheduleHydration is no longer gated through index.stable.js because its exposed through ReactDOMHydrationRoot instead of the ReactDOM package

* remove another gating
2021-11-15 17:15:01 -05:00
Andrew Clark 75f3ddebfa Remove experimental useOpaqueIdentifier API (#22672)
useId is the updated version of this API.
2021-11-01 15:02:39 -07:00
Sebastian Markbåge 34e4c97561 Clear extra nodes if there's a hydration mismatch within a suspense boundary (#22592)
* Clear extra nodes if there's a mismatch within a suspense boundary

This usually happens when we exit out a DOM node but a suspense boundary
is a virtual DOM node and we didn't do it in that case because we took a
short cut by calling resetHydrationState directly since we know we won't
need to pop.

* Tighten up the types of getFirstHydratableChild

We currently call getFirstHydratableChild to step into the children of
a suspense boundary. This can be a text node or a suspense boundary
which isn't compatible with getFirstHydratableChild, and we cheat the type.

This accidentally works because .firstChild always returns null on those
nodes in the DOM.

This just makes that explicit.
2021-10-19 16:28:01 -07:00
Sebastian MarkbågeandDan Abramov 2af4a79333 Hydrate using SuspenseComponent as the parent (#22582)
* Add a failing test for Suspense hydration

* Include salazarm's changes to the test

* The hydration parent of a suspense boundary should be the boundary itself

This eventually got set when we popped back out of its children but it
doesn't start out that way.

This fixes it so that the boundary parent is always the suspense boundary.

* We now need to log errors with a suspense boundary as a parent

For now, we just log this with commentNode.parentNode as the parent for
purposes of the error message.

* Make a special getFirstHydratableChildWithinSuspenseInstance

We currently call getNextHydratableSibling but conceptually it's the child
of the boundary. It just happens to be that when we use comment nodes, we
need to call nextSibling in the DOM.

This makes this separation a bit clearer.

* Sync old fork

Co-authored-by: Dan Abramov <dan.abramov@me.com>
2021-10-18 17:44:34 -07:00
Andrew Clark a724a3b578 [RFC] Codemod invariant -> throw new Error (#22435)
* Hoist error codes import to module scope

When this code was written, the error codes map (`codes.json`) was
created on-the-fly, so we had to lazily require from inside the visitor.

Because `codes.json` is now checked into source, we can import it a
single time in module scope.

* Minify error constructors in production

We use a script to minify our error messages in production. Each message
is assigned an error code, defined in `scripts/error-codes/codes.json`.
Then our build script replaces the messages with a link to our
error decoder page, e.g. https://reactjs.org/docs/error-decoder.html/?invariant=92

This enables us to write helpful error messages without increasing the
bundle size.

Right now, the script only works for `invariant` calls. It does not work
if you throw an Error object. This is an old Facebookism that we don't
really need, other than the fact that our error minification script
relies on it.

So, I've updated the script to minify error constructors, too:

Input:
  Error(`A ${adj} message that contains ${noun}`);
Output:
  Error(formatProdErrorMessage(ERR_CODE, adj, noun));

It only works for constructors that are literally named Error, though we
could add support for other names, too.

As a next step, I will add a lint rule to enforce that errors written
this way must have a corresponding error code.

* Minify "no fallback UI specified" error in prod

This error message wasn't being minified because it doesn't use
invariant. The reason it didn't use invariant is because this particular
error is created without begin thrown — it doesn't need to be thrown
because it's located inside the error handling part of the runtime.

Now that the error minification script supports Error constructors, we
can minify it by assigning it a production error code in
`scripts/error-codes/codes.json`.

To support the use of Error constructors more generally, I will add a
lint rule that enforces each message has a corresponding error code.

* Lint rule to detect unminified errors

Adds a lint rule that detects when an Error constructor is used without
a corresponding production error code.

We already have this for `invariant`, but not for regular errors, i.e.
`throw new Error(msg)`. There's also nothing that enforces the use of
`invariant` besides convention.

There are some packages where we don't care to minify errors. These are
packages that run in environments where bundle size is not a concern,
like react-pg. I added an override in the ESLint config to ignore these.

* Temporarily add invariant codemod script

I'm adding this codemod to the repo temporarily, but I'll revert it
in the same PR. That way we don't have to check it in but it's still
accessible (via the PR) if we need it later.

* [Automated] Codemod invariant -> Error

This commit contains only automated changes:

npx jscodeshift -t scripts/codemod-invariant.js packages --ignore-pattern="node_modules/**/*"
yarn linc --fix
yarn prettier

I will do any manual touch ups in separate commits so they're easier
to review.

* Remove temporary codemod script

This reverts the codemod script and ESLint config I added temporarily
in order to perform the invariant codemod.

* Manual touch ups

A few manual changes I made after the codemod ran.

* Enable error code transform per package

Currently we're not consistent about which packages should have their
errors minified in production and which ones should.

This adds a field to the bundle configuration to control whether to
apply the transform. We should decide what the criteria is going
forward. I think it's probably a good idea to minify any package that
gets sent over the network. So yes to modules that run in the browser,
and no to modules that run on the server and during development only.
2021-09-30 12:01:28 -07:00
Andrew Clark d3e0869324 Make root.unmount() synchronous (#22444)
* Move flushSync warning to React DOM

When you call in `flushSync` from an effect, React fires a warning. I've
moved the implementation of this warning out of the reconciler and into
React DOM.

`flushSync` is a renderer API, not an isomorphic API, because it has
behavior that was designed specifically for the constraints of React
DOM. The equivalent API in a different renderer may not be the same.
For example, React Native has a different threading model than the
browser, so it might not make sense to expose a `flushSync` API to the
JavaScript thread.

* Make root.unmount() synchronous

When you unmount a root, the internal state that React stores on the
DOM node is immediately cleared. So, we should also synchronously
delete the React tree. You should be able to create a new root using
the same container.
2021-09-27 14:04:39 -07:00
Justin Grant c88fb49d37 Improve DEV errors if string coercion throws (Temporal.*, Symbol, etc.) (#22064)
* Revise ESLint rules for string coercion

Currently, react uses `'' + value` to coerce mixed values to strings.
This code will throw for Temporal objects or symbols.

To make string-coercion safer and to improve user-facing error messages,
This commit adds a new ESLint rule called `safe-string-coercion`.

This rule has two modes: a production mode and a non-production mode.
* If the `isProductionUserAppCode` option is true, then `'' + value`
  coercions are allowed (because they're faster, although they may
  throw) and `String(value)` coercions are disallowed. Exception:
  when building error messages or running DEV-only code in prod
  files, `String()` should be used because it won't throw.
* If the `isProductionUserAppCode` option is false, then `'' + value`
  coercions are disallowed (because they may throw, and in non-prod
  code it's not worth the risk) and `String(value)` are allowed.

Production mode is used for all files which will be bundled with
developers' userland apps. Non-prod mode is used for all other React
code: tests, DEV blocks, devtools extension, etc.

In production mode, in addiiton to flagging `String(value)` calls,
the rule will also flag `'' + value` or `value + ''` coercions that may
throw. The rule is smart enough to silence itself in the following
"will never throw" cases:
* When the coercion is wrapped in a `typeof` test that restricts to safe
  (non-symbol, non-object) types. Example:
    if (typeof value === 'string' || typeof value === 'number') {
      thisWontReport('' + value);
    }
* When what's being coerced is a unary function result, because unary
   functions never return an object or a symbol.
* When the coerced value is a commonly-used numeric identifier:
  `i`, `idx`, or `lineNumber`.
* When the statement immeidately before the coercion is a DEV-only
  call to a function from shared/CheckStringCoercion.js. This call is a
  no-op in production, but in DEV it will show a console error
  explaining the problem, then will throw right after a long explanatory
  code comment so that debugger users will have an idea what's going on.
  The check function call must be in the following format:
    if (__DEV__) {
      checkXxxxxStringCoercion(value);
    };

Manually disabling the rule is usually not necessary because almost all
prod use of the `'' + value` pattern falls into one of the categories
above. But in the rare cases where the rule isn't smart enough to detect
safe usage (e.g. when a coercion is inside a nested ternary operator),
manually disabling the rule will be needed.

The rule should also be manually disabled in prod error handling code
where `String(value)` should be used for coercions, because it'd be
bad to throw while building an error message or stack trace!

The prod and non-prod modes have differentiated error messages to
explain how to do a proper coercion in that mode.

If a production check call is needed but is missing or incorrect
(e.g. not in a DEV block or not immediately before the coercion), then
a context-sensitive error message will be reported so that developers
can figure out what's wrong and how to fix the problem.

Because string coercions are now handled by the `safe-string-coercion`
rule, the `no-primitive-constructor` rule no longer flags `String()`
usage. It still flags `new String(value)` because that usage is almost
always a bug.

* Add DEV-only string coercion check functions

This commit adds DEV-only functions to check whether coercing
values to strings using the `'' + value` pattern will throw. If it will
throw, these functions will:
1. Display a console error with a friendly error message describing
   the problem and the developer can fix it.
2. Perform the coercion, which will throw. Right before the line where
   the throwing happens, there's a long code comment that will help
   debugger users (or others looking at the exception call stack) figure
   out what happened and how to fix the problem.

One of these check functions should be called before all string coercion
of user-provided values, except when the the coercion is guaranteed not
to throw, e.g.
* if inside a typeof check like `if (typeof value === 'string')`
* if coercing the result of a unary function like `+value` or `value++`
* if coercing a variable named in a whitelist of numeric identifiers:
  `i`, `idx`, or `lineNumber`.

The new `safe-string-coercion` internal ESLint rule enforces that
these check functions are called when they are required.

Only use these check functions in production code that will be bundled
with user apps.  For non-prod code (and for production error-handling
code), use `String(value)` instead which may be a little slower but will
never throw.

* Add failing tests for string coercion

Added failing tests to verify:
* That input, select, and textarea elements with value and defaultValue
  set to Temporal-like objects which will throw when coerced to string
  using the `'' + value` pattern.
* That text elements will throw for Temporal-like objects
* That dangerouslySetInnerHTML will *not* throw for Temporal-like
  objects because this value is not cast to a string before passing to
  the DOM.
* That keys that are Temporal-like objects will throw

All tests above validate the friendly error messages thrown.

* Use `String(value)` for coercion in non-prod files

This commit switches non-production code from `'' + value` (which
throws for Temporal objects and symbols) to instead use `String(value)`
which won't throw for these or other future plus-phobic types.

"Non-produciton code" includes anything not bundled into user apps:
* Tests and test utilities. Note that I didn't change legacy React
  test fixtures because I assumed it was good for those files to
  act just like old React, including coercion behavior.
* Build scripts
* Dev tools package - In addition to switching to `String`, I also
  removed special-case code for coercing symbols which is now
  unnecessary.

* Add DEV-only string coercion checks to prod files

This commit adds DEV-only function calls to to check if string coercion
using `'' + value` will throw, which it will if the value is a Temporal
object or a symbol because those types can't be added with `+`.

If it will throw, then in DEV these checks will show a console error
to help the user undertsand what went wrong and how to fix the
problem. After emitting the console error, the check functions will
retry the coercion which will throw with a call stack that's easy (or
at least easier!) to troubleshoot because the exception happens right
after a long comment explaining the issue. So whether the user is in
a debugger, looking at the browser console, or viewing the in-browser
DEV call stack, it should be easy to understand and fix the problem.

In most cases, the safe-string-coercion ESLint rule is smart enough to
detect when a coercion is safe. But in rare cases (e.g. when a coercion
is inside a ternary) this rule will have to be manually disabled.

This commit also switches error-handling code to use `String(value)`
for coercion, because it's bad to crash when you're trying to build
an error message or a call stack!  Because `String()` is usually
disallowed by the `safe-string-coercion` ESLint rule in production
code, the rule must be disabled when `String()` is used.
2021-09-27 10:05:07 -07:00
Andrew Clark 82c8fa90be Add back useMutableSource temporarily (#22396)
Recoil uses useMutableSource behind a flag. I thought this was fine
because Recoil isn't used in any concurrent roots, so the behavior
would be the same, but it turns out that it is used by concurrent
roots in a few places.

I'm not expecting it to be hard to migrate to useSyncExternalStore, but
to de-risk the change I'm going to roll it out gradually with a flag. In
the meantime, I've added back the useMutableSource API.
2021-09-21 20:38:24 -07:00
Andrew Clark 8209de2695 Delete useMutableSource implementation (#22292)
This API was replaced by useSyncExternalStore
2021-09-19 21:11:50 -07:00
Andrew Clark 5c6543771b Fix global reference to Promise (#21926)
Referencing Promise without a type check will throw in environments
where Promise is not defined.

We will follow up with a lint rule that restricts access to all globals
except in dedicated module indirections.
2021-07-20 15:39:57 -07:00
Ricky 682bbd0e72 Cache local reference to global Promise (#21886) 2021-07-15 12:39:56 -04:00
Andrew Clark 54e88ed12c Bugfix: Flush legacy sync passive effects at beginning of event (#21846)
* Re-land recent flushSync changes

Adds back #21776 and #21775, which were removed due to an internal
e2e test failure.

Will attempt to fix in subsequent commits.

* Failing test: Legacy mode sync passive effects

In concurrent roots, if a render is synchronous, we flush its passive
effects synchronously. In legacy roots, we don't do this because all
updates are synchronous — so we need to flush at the beginning of the
next event. This is how `discreteUpdates` worked.

* Flush legacy passive effects at beginning of event

Fixes test added in previous commit.
2021-07-10 11:15:11 -07:00
Brian Vaughn 9ccc25a0ea Reverting recent flushSync changes (#21816) 2021-07-07 15:07:55 -04:00
Andrew Clark ed6c091fe9 Replace unbatchedUpdates with flushSync (#21776)
There's a weird quirk leftover from the old Stack (pre-Fiber)
implementation where the initial mount of a leagcy (ReactDOM.render)
root is flushed synchronously even inside `batchedUpdates`.

The original workaround for this was an internal method called
`unbatchedUpdates`. We've since added another API that works almost the
same way, `flushSync`.

The only difference is that `unbatchedUpdates` would not cause other
pending updates to flush too, only the newly mounted root. `flushSync`
flushes all pending sync work across all roots. This was to preserve
the exact behavior of the Stack implementation.

But since it's close enough, let's just use `flushSync`. It's unlikely
anyone's app accidentally relies on this subtle difference, and the
legacy API is deprecated in 18, anyway.
2021-07-01 15:14:07 -07:00
Andrew Clark 32eefcb3c5 Replace flushDiscreteUpdates with flushSync (#21775)
* Replace flushDiscreteUpdates with flushSync

flushDiscreteUpdates is almost the same as flushSync. It forces passive
effects to fire, because of an outdated heuristic, which isn't ideal but
not that important.

Besides that, the only remaining difference between flushDiscreteUpdates
and flushSync is that flushDiscreteUpdates does not warn if you call it
from inside an effect/lifecycle. This is because it might get triggered
by a nested event dispatch, like `el.focus()`.

So I added a new method, flushSyncWithWarningIfAlreadyRendering, which
is used for the public flushSync API. It includes the warning. And I
removed the warning from flushSync, so the event system can call that
one. In production, flushSyncWithWarningIfAlreadyRendering gets inlined
to flushSync, so the behavior is identical.

Another way of thinking about this PR is that I renamed flushSync to
flushSyncWithWarningIfAlreadyRendering and flushDiscreteUpdates to
flushSync (and fixed the passive effects thing). The point is to prevent
these from subtly diverging in the future.

* Invert so the one with the warning is the default one

To make Seb happy
2021-07-01 15:13:00 -07:00
Andrew Clark c96761c7b2 Delete batchedEventUpdates (#21774)
No longer used anywhere.
2021-06-30 15:09:14 -07:00
Andrew Clark d7dce572c7 Remove internal act builds from public modules (#21721)
* Move internal version of act to shared module

No reason to have three different copies of this anymore.

I've left the the renderer-specific `act` entry points because legacy
mode tests need to also be wrapped in `batchedUpdates`. Next, I'll update
the tests to use `batchedUpdates` manually when needed.

* Migrates tests to use internal module directly

Instead of the `unstable_concurrentAct` exports. Now we can drop those
from the public builds.

I put it in the jest-react package since that's where we put our other
testing utilities (like `toFlushAndYield`). Not so much so it can be
consumed publicly (nobody uses that package except us), but so it works
with our build tests.

* Remove unused internal fields

These were used by the old act implementation. No longer needed.
2021-06-22 14:29:35 -07:00
Andrew Clark 06f7b4f43a act should work without mock Scheduler (#21714)
Currently, in a React 18 root, `act` only works if you mock the
Scheduler package. This was because we didn't want to add additional
checks at runtime.

But now that the `act` testing API is dev-only, we can simplify its
implementation.

Now when an update is wrapped with `act`, React will bypass Scheduler
entirely and push its tasks onto a special internal queue. Then, when
the outermost `act` scope exists, we'll flush that queue.

I also removed the "wrong act" warning, because the plan is to move
`act` to an isomorphic entry point, simlar to `startTransition`. That's
not directly related to this PR, but I didn't want to bother
re-implementing that warning only to immediately remove it.

I'll add the isomorphic API in a follow up.

Note that the internal version of `act` that we use in our own tests
still depends on mocking the Scheduler package, because it needs to work
in production. I'm planning to move that implementation to a shared
(internal) module, too.
2021-06-22 14:25:07 -07:00
Andrew Clark 422e0bb360 Delete test-utils implementation of act (#21703)
* Delete test-utils implementation of `act`

Since it's dev-only now, we can use the one provided by the reconciler.

* Move act related stuff out of EventInternals
2021-06-22 14:21:22 -07:00
Sebastian Markbåge 7ec4c55971 createRoot(..., {hydrate:true}) -> hydrateRoot(...) (#21687)
This adds a new top level API for hydrating a root. It takes the initial
children as part of its constructor. These are unlike other render calls
in that they have to represent what the server sent and they can't be
batched with other updates.

I also changed the options to move the hydrationOptions to the top level
since now these options are all hydration options.

I kept the createRoot one just temporarily to make it easier to codemod
internally but I'm doing a follow up to delete.

As part of this I un-dried a couple of paths. ReactDOMLegacy was intended
to be built on top of the new API but it didn't actually use those root
APIs because there are special paths. It also doesn't actually use most of
the commmon paths since all the options are ignored. It also made it hard
to add only warnings for legacy only or new only code paths.

I also forked the create/hydrate paths because they're subtly different
since now the options are different. The containers are also different
because I now error for comment nodes during hydration which just doesn't
work at all but eventually we'll error for all createRoot calls.

After some iteration it might make sense to break out some common paths but
for now it's easier to iterate on the duplicates.
2021-06-15 13:37:53 -07:00
Andrew Clark aecb3b6d11 Deprecate ReactDOM.render and ReactDOM.hydrate (#21652)
* Use existing test warning filter for server tests

We have a warning filter for our internal tests to ignore warnings
that are too noisy or that we haven't removed from our test suite yet:
shouldIgnoreConsoleError.

Many of our server rendering tests don't use this filter, though,
because it has its own special of asserting warnings.

So I added the warning filter to the server tests, too.

* Deprecate ReactDOM.render and ReactDOM.hydrate

These are no longer supported in React 18. They are replaced by the
`createRoot` API.

The warning includes a link to documentation of the new API. Currently
it redirects to the corresponding working group post. Here's the PR to
set up the redirect: https://github.com/reactjs/reactjs.org/pull/3730

Many of our tests still use ReactDOM.render. We will need to gradually
migrate them over to createRoot.

In the meantime, I added the warnings to our internal warning filter.
2021-06-09 13:46:55 -07:00
Sebastian Markbåge 5890e0e692 Remove data-reactroot from server rendering and hydration heuristic (#20996)
This was used to implicitly hydrate if you call ReactDOM.render.

We've had a warning to explicitly use ReactDOM.hydrate(...) instead of
ReactDOM.render(...). We can now remove this from the generated markup.
(And avoid adding it to Fizz.)

This is a little strange to do now since we're trying hard to make the
root API work the same.

But if we kept it, we'd need to keep it in the generated output which adds
unnecessary bytes. It also risks people relying on it, in the Fizz world
where as this is an opportunity to create that clean state.

We could possibly only keep it in the old server rendering APIs but then
that creates an implicit dependency between which server API and which
client API that you use. Currently you can really mix and match either way.
2021-05-13 10:18:21 -07:00
Brian Vaughn 2bf4805e4b Update entry point exports (#21488)
The following APIs have been added to the `react` stable entry point:
* `SuspenseList`
* `startTransition`
* `unstable_createMutableSource`
* `unstable_useMutableSource`
* `useDeferredValue`
* `useTransition`

The following APIs have been added or removed from the `react-dom` stable entry point:
* `createRoot`
* `unstable_createPortal` (removed)

The following APIs have been added to the `react-is` stable entry point:
* `SuspenseList`
* `isSuspenseList`

The following feature flags have been changed from experimental to true:
* `enableLazyElements`
* `enableSelectiveHydration`
* `enableSuspenseServerRenderer`
2021-05-12 11:28:14 -04:00
Sebastian Markbåge 8ea11306ad Allow complex objects as children of option only if value is provided (#21431) 2021-05-05 08:40:54 -07:00
Ricky e9a4a44aae Add back root override for strict mode (#21428)
* Add back root override for strict mode

* Switch flag to boolean

* Fix flow
2021-05-04 15:42:48 -04:00
Brian Vaughn 15fb8c3045 createRoot API is no longer strict by default (#21417) 2021-05-03 16:57:03 -04:00