Commit Graph
605 Commits
Author SHA1 Message Date
kassens 120239fa77 Remove React.createFactory (#27798)
`React.createFactory` has been long deprecated. This removes it for the
next release.

DiffTrain build for [2aed507a76](https://github.com/facebook/react/commit/2aed507a76a0b1524426c398897cbe47d80c51e5)
2024-03-29 20:34:34 +00:00
jackpope 2d29da831d Land enableNewBooleanProps everywhere (#28676)
Rolled out internally. Removing flag.

DiffTrain build for [6cd6ba703d](https://github.com/facebook/react/commit/6cd6ba703de77e332ab201518b6e30e47cd49aaf)
2024-03-29 20:07:15 +00:00
rickhanlonii fc94889933 Warn when using useFormState (#28668)
## Overview

useFormState has been replaced with useActionState. Warn when it's used.

Also removes the `experimental_useFormState` warnings.

DiffTrain build for [18812b645c](https://github.com/facebook/react/commit/18812b645c93a9c42f931fae57bbbab9c1f402b8)
2024-03-29 17:45:34 +00:00
sophiebits aa4213fc9b Revert "Remove zoom from special cases list" (#28673)
Reverts facebook/react#26631

This got specced: https://github.com/w3c/csswg-drafts/pull/9699

I left msZoom because it seems plausible someone will still be using it
for backwards compat.

DiffTrain build for [19c7c2929b](https://github.com/facebook/react/commit/19c7c2929be68f87cfa6b7947c4ab9ffc5608e48)
2024-03-29 17:44:00 +00:00
rickhanlonii b34287d736 Land enableCustomElementPropertySupport for React 19 (#27450)
We've rolled out this flag internally on WWW. This PR removed flag
`enableCustomElementPropertySupport`

Test plan:
 -- `yarn test`

Co-authored-by: Ricky Hanlon <rickhanlonii@gmail.com>

DiffTrain build for [eb510a3304](https://github.com/facebook/react/commit/eb510a33048fabd95d272b1c0b65f941e2909240)
2024-03-29 17:12:58 +00:00
kassens 9f9e660ceb Remove module pattern function component support (flag only) (#28671)
Remove module pattern function component support (flag only)

> This is a redo of #27742, but only including the flag removal,
excluding further simplifications.

The module pattern

```
function MyComponent() {
  return {
    render() {
      return this.state.foo
    }
  }
}
```

has been deprecated for approximately 5 years now. This PR removes
support for this pattern.

DiffTrain build for [a73c3450e1](https://github.com/facebook/react/commit/a73c3450e1b528fa6cb3e94fa4d4359c7a4b61f1)
2024-03-29 15:20:18 +00:00
rickhanlonii 87edb8c159 Revert "Remove module pattern function component support" (#28670)
This breaks internal tests, so must be something in the refactor. Since
it's the top commit let's revert and split into two PRs, one that
removes the flag and one that does the refactor, so we can find the bug.

DiffTrain build for [f269074723](https://github.com/facebook/react/commit/f2690747239533fa266612d2d4dd9ae88ea92fbc)
2024-03-29 14:15:23 +00:00
gnoff 60fecf15dd Remove module pattern function component support (#27742)
The module pattern

```
function MyComponent() {
  return {
    render() {
      return this.state.foo
    }
  }
}
```

has been deprecated for approximately 5 years now. This PR removes
support for this pattern. It also simplifies a number of code paths in
particular related to the concept of `IndeterminateComponent` types.

DiffTrain build for [cc56bed38c](https://github.com/facebook/react/commit/cc56bed38cbe5a5c76dfdc4e9c642fab4884a3fc)
2024-03-28 20:13:12 +00:00
rickhanlonii 69b2f2deb2 Noop unstable_batchedUpdates (#28120)
## Overview

`unstable_batchedUpdates` is effectively a no-op outside of legacy mode,
this PR makes it an actual no-op outside legacy mode.

DiffTrain build for [63651c49e0](https://github.com/facebook/react/commit/63651c49e068a04cdc6ee1e2fa9c6125167987d2)
2024-03-28 18:20:41 +00:00
gnoff 9d23ab1211 Add support for preload media to ReactDOM (#28635)
This PR adds support for `media` option to `ReactDOM.preload()`, which
is needed when images differ between screen sizes (for example mobile vs
desktop)

DiffTrain build for [78328c0c4d](https://github.com/facebook/react/commit/78328c0c4d70c9b9ee4ad2d6a2319c95e628dd2d)
2024-03-28 17:09:49 +00:00
rickhanlonii 66062f02d8 s/form state/action state (#28631)
Rename internals from "form state" to "action state"

DiffTrain build for [05797ccebd](https://github.com/facebook/react/commit/05797ccebd285999343ab4fb94eb542f84be23b1)
2024-03-28 15:42:51 +00:00
gnoff 2d8f86fbb4 [Fiber] Remove the digest property from errorInfo passed to onRecoverableError (#28222)
Removes the digest property from errorInfo passed to onRecoverableError
when handling an error propagated from the server. Previously we warned
in Dev but still provided the digest on the errorInfo object. This
change removes digest from error info but continues to warn if it is
accessed. The reason for retaining the warning is the version with the
warning was not released as stable but we will include this deprecated
removal in our next major so we should communicate this change at
runtime.

DiffTrain build for [299a9c0598](https://github.com/facebook/react/commit/299a9c0598576f7dba170771b1c0b821281b1e15)
2024-03-28 15:07:04 +00:00
sebmarkbage dec75c4f84 Don't log onRecoverableError if the current commit fail (#28665)
We didn't recover after all.

Currently we might log a recoverable error in the recovery pass. E.g.
the SSR server had an error. Then the client component fails to render
which errors again. This ends up double logging.

So if we fail to actually complete a fully successful commit, we ignore
any recoverable errors because we'll get real errors logged.

It's possible that this might cover up some other error that happened at
the same time.

DiffTrain build for [e10a7b5cd5](https://github.com/facebook/react/commit/e10a7b5cd541882a78ff659147c1a0294413ccb0)
2024-03-28 14:44:35 +00:00
sebmarkbage 3c7faae3ba Remove errorHydratingContainer (#28664)
I originally added this in #21021 but I didn't mention why and I don't
quite remember why. Maybe because there were no other message? However
at the time the recoverable errors mechanism didn't exist.

Today I believe all cases where this happens will trigger another
recoverable error. Namely these two:

https://github.com/facebook/react/blob/9f33f699e4f832971dc0f2047129f832655a3b6d/packages/react-reconciler/src/ReactFiberBeginWork.js#L1442-L1446

https://github.com/facebook/react/blob/9f33f699e4f832971dc0f2047129f832655a3b6d/packages/react-reconciler/src/ReactFiberBeginWork.js#L2962-L2965

Therefore this is just an extra unnecessary log.

DiffTrain build for [323b6e98a7](https://github.com/facebook/react/commit/323b6e98a76fe6ee721f10d327a9a682334d1a97)
2024-03-28 03:52:59 +00:00
rickhanlonii ab2961cf4d Dymanic favorSafetyOverHydrationPerf (#28663)
For rollout

DiffTrain build for [9f33f699e4](https://github.com/facebook/react/commit/9f33f699e4f832971dc0f2047129f832655a3b6d)
2024-03-27 21:49:44 +00:00
gnoff d78c623922 [react-dom] Remove findDOMNode from OSS builds (#28267)
In the next major `findDOMNode` is being removed. This PR removes the
API from the react-dom entrypoints for OSS builds and re-exposes the
implementation as part of internals.

`findDOMNode` is being retained for Meta builds and so all tests that
currently use it will continue to do so by accessing it from internals.
Once the replacement API ships in an upcoming minor any tests that were
using this API incidentally can be updated to use the new API and any
tests asserting `findDOMNode`'s behavior directly can stick around until
we remove it entirely (once Meta has moved away from it)

DiffTrain build for [9ad40b1440](https://github.com/facebook/react/commit/9ad40b1440a2c0b61530f3710e5dae3847611b9c)
2024-03-27 21:48:08 +00:00
gnoff a0d5dab6f7 [Fizz][Legacy] Remove renderToNodeStream (#28607)
Stacked on #28606

renderToNodeStream has been deprecated since React 18 with a warning
indicating users should upgrade to renderToPipeableStream. This change
removes renderToNodeStream

DiffTrain build for [8436bcca62](https://github.com/facebook/react/commit/8436bcca6287077a5409b3c9180f8af0361b16a5)
2024-03-27 19:07:24 +00:00
eps1lon 28afafe589 Deprecate act from react-dom/test-utils in favor of act from react (#28597)
DiffTrain build for [2b036d3f1f](https://github.com/facebook/react/commit/2b036d3f1f016fbe8b121d223d96f09e785b97e1)
2024-03-27 15:32:48 +00:00
sebmarkbage b758998431 Make onUncaughtError and onCaughtError Configurable (#28641)
Stacked on #28627.

This makes error logging configurable using these
`createRoot`/`hydrateRoot` options:

```
onUncaughtError(error: mixed, errorInfo: {componentStack?: ?string}) => void
onCaughtError(error: mixed, errorInfo: {componentStack?: ?string, errorBoundary?: ?React.Component<any, any>}) => void
onRecoverableError(error: mixed, errorInfo: {digest?: ?string, componentStack?: ?string}) => void
```

We already have the `onRecoverableError` option since before.

Overriding these can be used to implement custom error dialogs (with
access to the `componentStack`).

It can also be used to silence caught errors when testing an error
boundary or if you prefer not getting logs for caught errors that you've
already handled in an error boundary.

I currently expose the error boundary instance but I think we should
probably remove that since it doesn't make sense for non-class error
boundaries and isn't very useful anyway. It's also unclear what it
should do when an error is rethrown from one boundary to another.

Since these are public APIs now we can implement the
ReactFiberErrorDialog forks using these options at the roots of the
builds. So I unforked those files and instead passed a custom option for
the native and www builds.

To do this I had to fork the ReactDOMLegacy file into ReactDOMRootFB
which is a duplication but that will go away as soon as the FB fork is
the only legacy root.

DiffTrain build for [a053716077](https://github.com/facebook/react/commit/a0537160771bafae90c6fd3154eeead2f2c903e7)
2024-03-27 04:56:36 +00:00
rickhanlonii 83f2467a23 [Breaking] Remove disableJavaScriptURLs (#28615)
## Overview

This has landed, so we can remove the flag

## Changelog

This change blocks using javascript URLs such as:

```html
<a href="javascript:notfine">p0wned</a>
```

We previously announced dropping support for this via a warning:

> A future version of React will block javascript: URLs as a security
precaution. Use event handlers instead if you can. If you need to
generate unsafe HTML try using dangerouslySetInnerHTML instead.

DiffTrain build for [9f8daa6cb5](https://github.com/facebook/react/commit/9f8daa6cb5aae476cf54611874ea7522243c6ba6)
2024-03-27 03:50:35 +00:00
sebmarkbage 6294c34a42 [Fiber] Don't Rethrow Errors at the Root (#28627)
Stacked on top of #28498 for test fixes.

### Don't Rethrow

When we started React it was 1:1 setState calls a series of renders and
if they error, it errors where the setState was called. Simple. However,
then batching came and the error actually got thrown somewhere else.
With concurrent mode, it's not even possible to get setState itself to
throw anymore.

In fact, all APIs that can rethrow out of React are executed either at
the root of the scheduler or inside a DOM event handler.
If you throw inside a React.startTransition callback that's sync, then
that will bubble out of the startTransition but if you throw inside an
async callback or a useTransition we now need to handle it at the hook
site. So in 19 we need to make all React.startTransition swallow the
error (and report them to reportError).

The only one remaining that can throw is flushSync but it doesn't really
make sense for it to throw at the callsite neither because batching.
Just because something rendered in this flush doesn't mean it was
rendered due to what was just scheduled and doesn't mean that it should
abort any of the remaining code afterwards. setState is fire and forget.
It's send an instruction elsewhere, it's not part of the current
imperative code.

Error boundaries never rethrow. Since you should really always have
error boundaries, most of the time, it wouldn't rethrow anyway.

Rethrowing also actually currently drops errors on the floor since we
can only rethrow the first error, so to avoid that we'd need to call
reportError anyway. This happens in RN events.

The other issue with rethrowing is that it logs an extra console.error.
Since we're not sure that user code will actually log it anywhere we
still log it too just like we do with errors inside error boundaries
which leads all of these to log twice.
The goal of this PR is to never rethrow out of React instead, errors
outside of error boundaries get logged to reportError. Event system
errors too.

### Breaking Changes

The main thing this affects is testing where you want to inspect the
errors thrown. To make it easier to port, if you're inside `act` we
track the error into act in an aggregate error and then rethrow it at
the root of `act`. Unlike before though, if you flush synchronously
inside of act it'll still continue until the end of act before
rethrowing.

I expect most user code breakages would be to migrate from `flushSync`
to `act` if you assert on throwing.

However, in the React repo we also have `internalAct` and the
`waitForThrow` helpers. Since these have to use public production
implementations we track these using the global onerror or process
uncaughtException. Unlike regular act, includes both event handler
errors and onRecoverableError by default too. Not just render/commit
errors. So I had to account for that in our tests.

We restore logging an extra log for uncaught errors after the main log
with the component stack in it. We use `console.warn`. This is not yet
ignorable if you preventDefault to the main error event. To avoid
confusion if you don't end up logging the error to console I just added
`An error occurred`.

### Polyfill

All browsers we support really supports `reportError` but not all test
and server environments do, so I implemented a polyfill for browser and
node in `shared/reportGlobalError`. I don't love that this is included
in all builds and gets duplicated into isomorphic even though it's not
actually needed in production. Maybe in the future we can require a
polyfill for this.

### Follow Ups

In a follow up, I'll make caught vs uncaught error handling be
configurable too.

---------

Co-authored-by: Ricky Hanlon <rickhanlonii@gmail.com>

DiffTrain build for [6786563f3c](https://github.com/facebook/react/commit/6786563f3cbbc9b16d5a8187207b5bd904386e53)
2024-03-27 03:48:46 +00:00
sebmarkbage b8ac8ebbb3 Add Flag to Favor Hydration Performance over User Safety (#28655)
If false, this ignores text comparison checks during hydration at the
risk of privacy safety.

Since React 18 we recreate the DOM starting from the nearest Suspense
boundary if any of the text content mismatches. This ensures that if we
have nodes that otherwise line up correctly such as if they're the same
type of Component but in a different order, then we don't accidentally
transfer state or attributes to the wrong one.

If we didn't do this e.g. attributes like image src might not line up
with the text. E.g. you might show the wrong profile picture with the
wrong name. However, the main reason we do this is because it's a
security/privacy concern if state from the original node can transfer to
the other one. For example if you start typing into a text field to
reply to a story but then it turns out that the hydration was in a
different order, you might submit that text into a different story than
you intended. Similarly, if you've already clicked an item and that gets
replayed using Action replaying or is synchronously force hydrated -
that click might end up applying to a different item in the list than
you intended. E.g. liking the wrong photo.

Unfortunately a common case where this happens is when Google Translate
is applied to a page. It'll always cause mismatches and recreate the
tree. Most of the time this wouldn't be visible to users because it'd
just recreate to the same thing and then translate again. It can affect
metrics that trace when this hydration happened though.

Meta can use this flag to decide if they favor this perf metric over the
risk to user privacy.

This is similar to the old enableClientRenderFallbackOnTextMismatch flag
except this flag doesn't patch up the text when there's a mismatch.
Because we don't have the patching anymore. The assumption is that it is
safe to ignore the safety concern because we assume it's a match and
therefore favoring not patching it will lead to better perf.

DiffTrain build for [5910eb3456](https://github.com/facebook/react/commit/5910eb34567a8699d1faa73b546baafd94f26411)
2024-03-27 02:58:26 +00:00
sebmarkbage 1fc73fbd8f Add Diffs to Hydration Warnings (#28512)
Stacked on #28502.

This builds on the mechanism in #28502 by adding a diff of everything
we've collected so far to the thrown error or logged error.

This isn't actually a longest common subsequence diff. This means that
there are certain cases that can appear confusing such as a node being
added/removed when it really would've appeared later in the list. In
fact once a node mismatches, we abort rendering so we don't have the
context of what would've been rendered. It's not quite right to use the
result of the recovery render because it can use client-only code paths
using useSyncExternalStore which would yield false differences. That's
why diffing the HTML isn't quite right.

I also present abstract components in the stack, these are presented
with the client props and no diff since we don't have the props that
were on the server. The lack of difference might be confusing but it's
useful for context.

The main thing that's data new here is that we're adding some siblings
and props for context.

Examples in the [snapshot
commit](https://github.com/facebook/react/pull/28512/commits/e14532fd8df858a319d3eca687d83227209a498c).

DiffTrain build for [2ec2aaea98](https://github.com/facebook/react/commit/2ec2aaea98588178525f83495669e11e96815a00)
2024-03-27 00:08:21 +00:00
sebmarkbage 5add535517 Move Hydration Mismatch Errors to Throw or Log Once (Kind of) (#28502)
Stacked on #28476.

We used to `console.error` for every mismatch we found, up until the
error we threw for the hydration mismatch.

This changes it so that we build up a set of diffs up until we either
throw or complete hydrating the root/suspense boundary. If we throw, we
append the diff to the error message which gets passed to
onRecoverableError (which by default is also logged to console). If we
complete, we append it to a `console.error`.

Since we early abort when something throws, it effectively means that we
can only collect multiple diffs if there were preceding non-throwing
mismatches - i.e. only properties mismatched but tag name matched.

There can still be multiple logs if multiple siblings Suspense
boundaries all error hydrating but then they're separate errors
entirely.

We still log an extra line about something erroring but I think the goal
should be that it leads to a single recoverable or console.error.

This doesn't yet actually print the diff as part of this message. That's
in a follow up PR.

DiffTrain build for [f7aa5e0aa3](https://github.com/facebook/react/commit/f7aa5e0aa3e2aa51279af4b6cb5413912cacd7f5)
2024-03-27 00:06:40 +00:00
sebmarkbage 0602001f3a Move Hydration Warnings from the DOM Config into the Fiber reconciliation (#28476)
Stacked on #28458.

This doesn't actually really change the messages yet, it's just a
refactor.

Hydration warnings can be presented either as HTML or React JSX format.
If presented as HTML it makes more sense to make that a DOM specific
concept, however, I think it's actually better to present it in terms of
React JSX.

Most of the time the errors aren't going to be something messing with
them at the HTML/HTTP layer. It's because the JS code does something
different. Most of the time you're working in just React. People don't
necessarily even know what the HTML form of it looks like. So this takes
the approach that the warnings are presented in React JSX in their rich
object form.

Therefore, I'm moving the approach to yield diff data to the reconciler
but it's the reconciler that's actually printing all the warnings.

DiffTrain build for [4b8dfd6215](https://github.com/facebook/react/commit/4b8dfd6215bf855402ae1a94cb0ae4f467afca9a)
2024-03-26 23:09:12 +00:00
jackpope 34ee899fef Use concurrent root in RTR (#28498)
Based on
- https://github.com/facebook/react/pull/28497
- https://github.com/facebook/react/pull/28419

Reusing the disableLegacyMode flag, we set ReactTestRenderer to always
render with concurrent root where legacy APIs are no longer available.
If disableLegacyMode is false, we continue to allow the
unstable_isConcurrent option determine the root type.

Also checking a global `IS_REACT_NATIVE_TEST_ENVIRONMENT` so we can
maintain the existing behavior for RN until we remove legacy root
support there.

DiffTrain build for [bb66aa3cef](https://github.com/facebook/react/commit/bb66aa3cef4e42aee790200d03cf7a82659da121)
2024-03-26 22:57:58 +00:00
sebmarkbage 71cc6ab810 Remove enableClientRenderFallbackOnTextMismatch flag (#28458)
Build on top of #28440.

This lets us remove the path where updates are tracked on differences in
text.

DiffTrain build for [84c84d72f1](https://github.com/facebook/react/commit/84c84d72f11ff1961a103b3cd59919876e48f759)
2024-03-26 22:00:41 +00:00
sebmarkbage 8ee89e3444 Remove legacy hydration mode (#28440)
While Meta is still using legacy mode and we can't remove completely,
Meta is not using legacy hydration so we should be able to remove that.

This is just the first step. Once removed, we can vastly simplify the
DOMConfig for hydration.

This will have to be rebased when tests are upgraded.

DiffTrain build for [670d61bea2](https://github.com/facebook/react/commit/670d61bea23470e980ba13c1c8441e375779b0b8)
2024-03-26 21:46:39 +00:00
rickhanlonii eb7de0442f Update error messages (#28652)
## Overview

The error messages that say:

> ReactDOM.hydrate is no longer supported in React 18

Don't make sense in the React 19 release. Instead, they should say:

> ReactDOM.hydrate was removed in React 19.

For legacy mode, they should say:

> ReactDOM.hydrate has not been supported since React 18.

DiffTrain build for [dbfbfb3312](https://github.com/facebook/react/commit/dbfbfb3312db019183ef92fd2ef110cc7d807e80)
2024-03-26 21:30:55 +00:00
sebmarkbage 17f0c67368 Remove zoom from special cases list (#26631)
Looks like [it's getting removed
anyway](https://groups.google.com/a/chromium.org/g/blink-dev/c/V7q43bgutbo/m/-7jneTl8CQAJ?pli=1).

Maybe should wait for a major though.

DiffTrain build for [f24cf4a1af](https://github.com/facebook/react/commit/f24cf4a1af4e677dd85fce66001eee42b75b1cf7)
2024-03-26 21:23:47 +00:00
rickhanlonii 66f3d77750 Remove unmountComponentAtNode outside of legacy mode (#28650)
DiffTrain build for [1a6d36b1a3](https://github.com/facebook/react/commit/1a6d36b1a3ec43cb5700e28d7315b3aa2822365d)
2024-03-26 20:37:28 +00:00
acdlite a6c58b8c4a Bump canary versions to v19-canary (#28646)
This bumps the canary versions to v19 to communicate that the next
release will be a major. Once this lands, we can start merging breaking
changes into `main`.

DiffTrain build for [56efb2e227](https://github.com/facebook/react/commit/56efb2e2271492288b1ba37c247df2a65d9046b7)
2024-03-26 19:35:48 +00:00
yungsters ed3c6afae1 Cleanup alwaysThrottleDisappearingFallbacks Flag (#28639)
## Summary

After realizing that this feature flag is entangled with
`alwaysThrottleRetries`, we're going to undo
https://github.com/facebook/react/pull/28550

## How did you test this change?

```
$ yarn test
$ yarn flow dom-browser
$ yarn flow dom-fb
$ yarn flow fabric
```

DiffTrain build for [6fd0cb9a9f](https://github.com/facebook/react/commit/6fd0cb9a9feeccea2f70d993dc2d53f11cab0b5d)
2024-03-26 17:06:43 +00:00
kassens fc5d9dbf94 Switch facebook-www build version to file content hash (#28633)
Unifies the React version string pattern with RN just to simplify the
build script a tiny bit and not have 2 mechanisms for the Meta-internal
build.

DiffTrain build for [95319ab5af](https://github.com/facebook/react/commit/95319ab5afd384f5858f7c080573b9736e6b2f9c)
2024-03-26 16:18:51 +00:00
kassens 07e6503fe1 Make enableBigIntSupport a dynamic flag for Meta (#28617)
Make enableBigIntSupport a dynamic flag for Meta

Should be an easy launch, but let's make this a dynamic flag to be safe.

DiffTrain build for [5a75f9e785](https://github.com/facebook/react/commit/5a75f9e78544fa6d052aff7fe99607e48f35b979)
2024-03-25 17:55:21 +00:00
kassens 97218a1e39 Cleanup enableFormActions flag (#28614)
Cleanup enableFormActions flag

DiffTrain build for [527ed72bfd](https://github.com/facebook/react/commit/527ed72bfd9f5c0045da468ad01db968a9961ad7)
2024-03-25 17:30:23 +00:00
rickhanlonii 01f55b75d9 Add React.useActionState (#28491)
## Overview

_Depends on https://github.com/facebook/react/pull/28514_

This PR adds a new React hook called `useActionState` to replace and
improve the ReactDOM `useFormState` hook.

## Motivation

This hook intends to fix some of the confusion and limitations of the
`useFormState` hook.

The `useFormState` hook is only exported from the `ReactDOM` package and
implies that it is used only for the state of `<form>` actions, similar
to `useFormStatus` (which is only for `<form>` element status). This
leads to understandable confusion about why `useFormState` does not
provide a `pending` state value like `useFormStatus` does.

The key insight is that the `useFormState` hook does not actually return
the state of any particular form at all. Instead, it returns the state
of the _action_ passed to the hook, wrapping it and returning a
trackable action to add to a form, and returning the last returned value
of the action given. In fact, `useFormState` doesn't need to be used in
a `<form>` at all.

Thus, adding a `pending` value to `useFormState` as-is would thus be
confusing because it would only return the pending state of the _action_
given, not the `<form>` the action is passed to. Even if we wanted to
tie them together, the returned `action` can be passed to multiple
forms, creating confusing and conflicting pending states during multiple
form submissions.

Additionally, since the action is not related to any particular
`<form>`, the hook can be used in any renderer - not only `react-dom`.
For example, React Native could use the hook to wrap an action, pass it
to a component that will unwrap it, and return the form result state and
pending state. It's renderer agnostic.

To fix these issues, this PR:
- Renames `useFormState` to `useActionState`
- Adds a `pending` state to the returned tuple
- Moves the hook to the `'react'` package

## Reference

The `useFormState` hook allows you to track the pending state and return
value of a function (called an "action"). The function passed can be a
plain JavaScript client function, or a bound server action to a
reference on the server. It accepts an optional `initialState` value
used for the initial render, and an optional `permalink` argument for
renderer specific pre-hydration handling (such as a URL to support
progressive hydration in `react-dom`).

Type:

```ts
function useActionState<State>(
        action: (state: Awaited<State>) => State | Promise<State>,
        initialState: Awaited<State>,
        permalink?: string,
    ): [state: Awaited<State>, dispatch: () => void, boolean];
```

The hook returns a tuple with:
- `state`: the last state the action returned
- `dispatch`: the method to call to dispatch the wrapped action
- `pending`: the pending state of the action and any state updates
contained

Notably, state updates inside of the action dispatched are wrapped in a
transition to keep the page responsive while the action is completing
and the UI is updated based on the result.

## Usage

The `useActionState` hook can be used similar to `useFormState`:

```js
import { useActionState } from "react"; // not react-dom

function Form({ formAction }) {
  const [state, action, isPending] = useActionState(formAction);

  return (
    <form action={action}>
      <input type="email" name="email" disabled={isPending} />
      <button type="submit" disabled={isPending}>
        Submit
      </button>
      {state.errorMessage && <p>{state.errorMessage}</p>}
    </form>
  );
}
```

But it doesn't need to be used with a `<form/>` (neither did
`useFormState`, hence the confusion):

```js
import { useActionState, useRef } from "react";

function Form({ someAction }) {
  const ref = useRef(null);
  const [state, action, isPending] = useActionState(someAction);

  async function handleSubmit() {
    // See caveats below
    await action({ email: ref.current.value });
  }

  return (
    <div>
      <input ref={ref} type="email" name="email" disabled={isPending} />
      <button onClick={handleSubmit} disabled={isPending}>
        Submit
      </button>
      {state.errorMessage && <p>{state.errorMessage}</p>}
    </div>
  );
}
```

## Benefits

One of the benefits of using this hook is the automatic tracking of the
return value and pending states of the wrapped function. For example,
the above example could be accomplished via:

```js
import { useActionState, useRef } from "react";

function Form({ someAction }) {
  const ref = useRef(null);
  const [state, setState] = useState(null);
  const [isPending, setIsPending] = useTransition();

  function handleSubmit() {
    startTransition(async () => {
      const response = await someAction({ email: ref.current.value });
      setState(response);
    });
  }

  return (
    <div>
      <input ref={ref} type="email" name="email" disabled={isPending} />
      <button onClick={handleSubmit} disabled={isPending}>
        Submit
      </button>
      {state.errorMessage && <p>{state.errorMessage}</p>}
    </div>
  );
}
```

However, this hook adds more benefits when used with render specific
elements like react-dom `<form>` elements and Server Action. With
`<form>` elements, React will automatically support replay actions on
the form if it is submitted before hydration has completed, providing a
form of partial progressive enhancement: enhancement for when javascript
is enabled but not ready.

Additionally, with the `permalink` argument and Server Actions,
frameworks can provide full progressive enhancement support, submitting
the form to the URL provided along with the FormData from the form. On
submission, the Server Action will be called during the MPA navigation,
similar to any raw HTML app, server rendered, and the result returned to
the client without any JavaScript on the client.

## Caveats
There are a few Caveats to this new hook:
**Additional state update**: Since we cannot know whether you use the
pending state value returned by the hook, the hook will always set the
`isPending` state at the beginning of the first chained action,
resulting in an additional state update similar to `useTransition`. In
the future a type-aware compiler could optimize this for when the
pending state is not accessed.

**Pending state is for the action, not the handler**: The difference is
subtle but important, the pending state begins when the return action is
dispatched and will revert back after all actions and transitions have
settled. The mechanism for this under the hook is the same as
useOptimisitic.

Concretely, what this means is that the pending state of
`useActionState` will not represent any actions or sync work performed
before dispatching the action returned by `useActionState`. Hopefully
this is obvious based on the name and shape of the API, but there may be
some temporary confusion.

As an example, let's take the above example and await another action
inside of it:

```js
import { useActionState, useRef } from "react";

function Form({ someAction, someOtherAction }) {
  const ref = useRef(null);
  const [state, action, isPending] = useActionState(someAction);

  async function handleSubmit() {
    await someOtherAction();

    // The pending state does not start until this call.
    await action({ email: ref.current.value });
  }

  return (
    <div>
      <input ref={ref} type="email" name="email" disabled={isPending} />
      <button onClick={handleSubmit} disabled={isPending}>
        Submit
      </button>
      {state.errorMessage && <p>{state.errorMessage}</p>}
    </div>
  );
}

```

Since the pending state is related to the action, and not the handler or
form it's attached to, the pending state only changes when the action is
dispatched. To solve, there are two options.

First (recommended): place the other function call inside of the action
passed to `useActionState`:

```js
import { useActionState, useRef } from "react";

function Form({ someAction, someOtherAction }) {
  const ref = useRef(null);
  const [state, action, isPending] = useActionState(async (data) => {
    // Pending state is true already.
    await someOtherAction();
    return someAction(data);
  });

  async function handleSubmit() {
    // The pending state starts at this call.
    await action({ email: ref.current.value });
  }

  return (
    <div>
      <input ref={ref} type="email" name="email" disabled={isPending} />
      <button onClick={handleSubmit} disabled={isPending}>
        Submit
      </button>
      {state.errorMessage && <p>{state.errorMessage}</p>}
    </div>
  );
}
```

For greater control, you can also wrap both in a transition and use the
`isPending` state of the transition:

```js
import { useActionState, useTransition, useRef } from "react";

function Form({ someAction, someOtherAction }) {
  const ref = useRef(null);

  // isPending is used from the transition wrapping both action calls.
  const [isPending, startTransition] = useTransition();

  // isPending not used from the individual action.
  const [state, action] = useActionState(someAction);

  async function handleSubmit() {
    startTransition(async () => {
      // The transition pending state has begun.
      await someOtherAction();
      await action({ email: ref.current.value });
    });
  }

  return (
    <div>
      <input ref={ref} type="email" name="email" disabled={isPending} />
      <button onClick={handleSubmit} disabled={isPending}>
        Submit
      </button>
      {state.errorMessage && <p>{state.errorMessage}</p>}
    </div>
  );
}
```

A similar technique using `useOptimistic` is preferred over using
`useTransition` directly, and is left as an exercise to the reader.

## Thanks

Thanks to @ryanflorence @mjackson @wesbos
(https://github.com/facebook/react/issues/27980#issuecomment-1960685940)
and [Allan
Lasser](https://allanlasser.com/posts/2024-01-26-avoid-using-reacts-useformstatus)
for their feedback and suggestions on `useFormStatus` hook.

DiffTrain build for [5c65b27587](https://github.com/facebook/react/commit/5c65b27587c0507d66a84e055de948fc62d471d4)
2024-03-22 17:08:27 +00:00
kassens 417f54eed6 Cleanup enableFloat flag (#28613)
Cleanup enableFloat flag

DiffTrain build for [208ceeb46c](https://github.com/facebook/react/commit/208ceeb46ca2838c9bf24cd341435f87b2d50569)
2024-03-22 16:27:43 +00:00
sebmarkbage 361d5a6d50 [Fizz] Recover from errors thrown by progressive enhancement form generation (#28611)
This a follow up to #28564. It's alternative to #28609 which takes
#28610 into account.

It used to be possible to return JSX from an action with
`useActionState`.

```js
async function action(errors, payload) {
  "use server";
  try {
    ...
  } catch (x) {
    return <div>Error message</div>;
  }
}
```

```js
const [errors, formAction] = useActionState(action);
return <div>{errors}</div>;
```

Returning JSX from an action is itself not anything problematic. It's
that it also has to return the previous state to the action reducer
again that's the problem. When this happens we accidentally could
serialize an Element back to the server.

I fixed this in #28564 so it's now blocked if you don't have a temporary
reference set.

However, you can't have that for the progressive enhancement case. The
reply is eagerly encoded as part of the SSR render. Typically you
wouldn't have these in the initial state so the common case is that they
show up after the first POST back that yields an error and it's only in
the no-JS case where this happens so it's hard to discover.

As noted in #28609 there's a security implication with allowing elements
to be sent across this kind of payload, so we can't just make it work.

When an error happens during SSR our general policy is to try to recover
on the client instead. After all, SSR is mainly a perf optimization in
React terms and it's not primarily intended for a no JS solution.

This PR takes the approach that if we fail to generate the progressive
enhancement payload. I.e. if the serialization of previous state /
closures throw. Then we fallback to the replaying semantics just client
actions instead which will succeed.

The effect of this is that this pattern mostly just works:

- First render in the typical case doesn't have any JSX in it so it just
renders a progressive enhanced form.
- If JS fails to hydrate or you click early we do a form POST. If that
hits an error and it tries to render it using JSX, then the new page
will render successfully - however this time with a Replaying form
instead.
- If you try to submit the form again it'll have to be using JS.

Meaning if you use JSX as the error return value of form state and you
make a first attempt that fails, then no JS won't work because either
the first or second attempt has to hydrate.

We have ideas for potentially optimizing away serializing unused
arguments like if you don't actually use previous state which would also
solve it but it wouldn't cover all cases such as if it was deeply nested
in complex state.

Another approach that I considered was to poison the prev state if you
passed an element back but let it through to the action but if you try
to render the poisoned value, it wouldn't work. The downside of this is
when to error. Because in the progressive enhancement case it wouldn't
error early but when you actually try to invoke it at which point it
would be too late to fallback to client replaying. It would probably
have to always error even on the client which is unfortunate since this
mostly just works as long as it hydrates.

DiffTrain build for [fee786a057](https://github.com/facebook/react/commit/fee786a057774ab687aff765345dd86fce534ab2)
2024-03-21 23:56:39 +00:00
eps1lon 6e5eaa6c92 Ensure dispatch from useFormState works in StrictMode (#28557)
Co-authored-by: Andrew Clark <git@andrewclark.io>

DiffTrain build for [8ef14cf242](https://github.com/facebook/react/commit/8ef14cf24219addedca3607dabb3bef37fb2e013)
2024-03-20 15:20:09 +00:00
sebmarkbage cc0b6cd97a [Flight] Encode React Elements in Replies as Temporary References (#28564)
Currently you can accidentally pass React Element to a Server Action. It
warns but in prod it actually works because we can encode the symbol and
otherwise it's mostly a plain object. It only works if you only pass
host components and no function props etc. which makes it potentially
error later. The first thing this does it just early hard error for
elements.

I made Lazy work by unwrapping though since that will be replaced by
Promises later which works.

Our protocol is not fully symmetric in that elements flow from Server ->
Client. Only the Server can resolve Components and only the client
should really be able to receive host components. It's not intended that
a Server can actually do something with them other than passing them to
the client.

In the case of a Reply, we expect the client to be stateful. It's
waiting for a response. So anything we can't serialize we can still pass
by reference to an in memory object. So I introduce the concept of a
TemporaryReferenceSet which is an opaque object that you create before
encoding the reply. This then stashes any unserializable values in this
set and encode the slot by id. When a new response from the Action then
returns we pass the same temporary set into the parser which can then
restore the objects. This lets you pass a value by reference to the
server and back into another slot.

For example it can be used to render children inside a parent tree from
a server action:

```
export async function Component({ children }) {
  "use server";
  return <div>{children}</div>;
}
```

(You wouldn't normally do this due to the waterfalls but for advanced
cases.)

A common scenario where this comes up accidentally today is in
`useActionState`.

```
export function action(state, formData) {
  "use server";
   if (errored) {
     return <div>This action <strong>errored</strong></div>;
   }
   return null;
}
```

```
const [errors, formAction] = useActionState(action);
return <div>{errors}<div>;
```

It feels like I'm just passing the JSX from server to client. However,
because `useActionState` also sends the previous state *back* to the
server this should not actually be valid. Before this PR this actually
worked accidentally. You get a DEV warning but it used to work in prod.
Once you do something like pass a client reference it won't work tho. We
could perhaps make client references work by stashing where we got them
from but it wouldn't work with all possible JSX.

By adding temporary references to the action implementation this will
work again - on the client. It'll also be more efficient since we don't
send back the JSX content that you shouldn't introspect on the server
anyway.

However, a flaw here is that the progressive enhancement of this case
won't work because we can't use temporary references for progressive
enhancement since there's no in memory stash. What is worse is that it
won't error if you hydrate. ~It also will error late in the example
above because the first state is "undefined" so invoking the form once
works - it errors on the second attempt when it tries to send the error
state back again.~ It actually errors on the first invocation because we
need to eagerly serialize "previous state" into the form. So at least
that's better.

I think maybe the solution to this particular pattern would be to allow
JSX to serialize if you have no temporary reference set, and remember
client references so that client references can be returned back to the
server as client references. That way anything you could send from the
server could also be returned to the server. But it would only deopt to
serializing it for progressive enhancement. The consequence of that
would be that there's a lot of JSX that might accidentally seem like it
should work but it's only if you've gotten it from the server before
that it works. This would have to have pair them somehow though since
you can't take a client reference from one implementation of Flight and
use it with another.

DiffTrain build for [83409a1fdd](https://github.com/facebook/react/commit/83409a1fdd14b2e5b33c587935a7ef552607780f)
2024-03-19 21:04:50 +00:00
yungsters 8cc2b728f0 Add alwaysThrottleDisappearingFallbacks Flag (#28550)
## Summary

Creates a new `alwaysThrottleDisappearingFallbacks` feature flag that
gates the changes from https://github.com/facebook/react/pull/26802
(instead of being controlled by `alwaysThrottleRetries`). The values of
this new flag mirror the current values of `alwaysThrottleRetries` such
that there is no behavior difference.

This additional feature flag allows us to incrementally validate the
change (arguably bug fix) from
https://github.com/facebook/react/pull/26802 independently from
`alwaysThrottleRetries`.

## How did you test this change?

```
$ yarn test
$ yarn flow dom-browser
$ yarn flow dom-fb
$ yarn flow fabric
```

DiffTrain build for [0aab065eb3](https://github.com/facebook/react/commit/0aab065eb3250a9714a62dc05587cbb571da7f71)
2024-03-18 18:20:35 +00:00
kassens 187dc167cd Performance: avoid triggering map deopt in V8 (#28569)
The shape of the objects changed by this PR are both created in 2
locations with 2 different shapes, which most JS engines won't like.
I've noticed this in particular in V8 while benchmarking production
code.

https://github.com/facebook/react/blob/1293047d6063f3508af15e68cca916660ded791e/packages/react-reconciler/src/ReactFiberCacheComponent.js#L66-L77

https://github.com/facebook/react/blob/1293047d6063f3508af15e68cca916660ded791e/packages/react-reconciler/src/ReactFiberHostContext.js#L47-L54

https://github.com/facebook/react/blob/1293047d6063f3508af15e68cca916660ded791e/packages/react-reconciler/src/ReactFiberHooks.js#L3530-L3531

https://github.com/facebook/react/blob/1293047d6063f3508af15e68cca916660ded791e/packages/react-reconciler/src/ReactFiberHooks.js#L3492-L3493

DiffTrain build for [4d686a2da1](https://github.com/facebook/react/commit/4d686a2da138cad9e76c7de9feadd81ca2aa29e5)
2024-03-18 15:58:28 +00:00
jackpope 0ef0a51c6c Update isConcurrent RTR option usage (#28546)
Reverting some of https://github.com/facebook/react/pull/27804 which
renamed this option to stable. This PR just replaces internal usage to
make upcoming PRs cleaner.

Keeping isConcurrent unstable for the next major release in order to
enable a broader deprecation of RTR and be consistent with concurrent
rendering everywhere for next major.
(https://github.com/facebook/react/pull/28498)
- Next major will use concurrent root
- The old behavior (legacy root by default, concurrent root with
unstable option) will be preserved for React Native until new
architecture is fully shipped.
- Flag and legacy root usage can be removed after RN dependency is
unblocked without an additional breaking change

DiffTrain build for [38327309a4](https://github.com/facebook/react/commit/38327309a4172a52cf94e8e264990bedafc1df6e)
2024-03-18 15:39:12 +00:00
gnoff d06d633cdb [Fizz] Prevent uncloned large precomputed chunks without relying on render-time assertions (#28568)
A while back we implemented a heuristic that if a chunk was large it was
assumed to be produced by the render and thus was safe to stream which
results in transferring the underlying object memory. Later we ran into
an issue where a precomputed chunk grew large enough to trigger this
hueristic and it started causing renders to fail because once a second
render had occurred the precomputed chunk would not have an underlying
buffer of bytes to send and these bytes would be omitted from the
stream. We implemented a technique to detect large precomputed chunks
and we enforced that these always be cloned before writing.
Unfortunately our test coverage was not perfect and there has been for a
very long time now a usage pattern where if you complete a boundary in
one flush and then complete a boundary that has stylehsheet dependencies
in another flush you can get a large precomputed chunk that was not
being cloned to be sent twice causing streaming errors.

I've thought about why we even went with this solution in the first
place and I think it was a mistake. It relies on a dev only check to
catch paired with potentially version specific order of operations on
the streaming side. This is too unreliable. Additionally the low limit
of view size for Edge is not used in Node.js but there is not real
justification for this.

In this change I updated the view size for edge streaming to match Node
at 2048 bytes which is still relatively small and we have no data one
way or another to preference 512 over this. Then I updated the assertion
logic to error anytime a precomputed chunk exceeds the size. This
eliminates the need to clone these chunks by just making sure our view
size is always larger than the largest precomputed chunk we can possibly
write. I'm generally in favor of this for a few reasons.

First, we'll always know during testing whether we've violated the limit
as long as we exercise each stream config because the precomputed chunks
are created in module scope. Second, we can always split up large chunks
so making sure the precomptued chunk is smaller than whatever view size
we actually desire is relatively trivial.

DiffTrain build for [b09e102ff1](https://github.com/facebook/react/commit/b09e102ff1e2aaaf5eb6585b04609ac7ff54a5c8)
2024-03-16 19:43:36 +00:00
jackpope cb7bf992ee Make enableNewBooleanProps www dynamic (#28559)
Feature: https://github.com/facebook/react/pull/24730

DiffTrain build for [a870b2d549](https://github.com/facebook/react/commit/a870b2d5494351d75b68c3d9baf03a52fd40a8ef)
2024-03-14 15:54:04 +00:00
kassens 809d7d488a Fix a spelling mistake. (#28555)
DiffTrain build for [9ffe9102ff](https://github.com/facebook/react/commit/9ffe9102ffd08ca7a56c60aa6952208890d213ce)
2024-03-14 14:08:59 +00:00
eps1lon b18936efc1 React DOM: Support boolean values for inert prop (#24730)
DiffTrain build for [bbc571aee4](https://github.com/facebook/react/commit/bbc571aee431d44799ae6a70832ea834325a5af9)
2024-03-13 22:14:51 +00:00
rickhanlonii 8e12d48171 Add pending state to useFormState (#28514)
## Overview

Adds a `pending` state to useFormState, which will be replaced by
`useActionState` in the next diff. We will keep `useFormState` around
for backwards compatibility, but functionally it will work the same as
`useActionState`, which has an `isPending` state returned.

DiffTrain build for [17eaacaac1](https://github.com/facebook/react/commit/17eaacaac167addf0c4358b4983f054073a0626d)
2024-03-12 19:54:22 +00:00
kassens 43ce284c19 Revert "Land enableClientRenderFallbackOnTextMismatch for www" (#28548)
We're looking to run further experiments on this.

Reverts facebook/react#28538

DiffTrain build for [3e6bc7d2d7](https://github.com/facebook/react/commit/3e6bc7d2d7098d9c8a30ba6e7a877df70f4d8d34)
2024-03-12 19:33:26 +00:00