Commit Graph
19463 Commits
Author SHA1 Message Date
Sebastian Markbåge 4b8dfd6215 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.
2024-03-26 19:04:18 -04:00
Jack Pope bb66aa3cef 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.
2024-03-26 18:53:09 -04:00
Jack Pope 1f9befef5c Remove react-test-renderer/shallow export (#28497)
Based on
- https://github.com/facebook/react/pull/28419

## Summary

The shallow renderer was extracted from the repo years ago and published
by enzyme: https://github.com/enzymejs/react-shallow-renderer

We no longer need to reexport under the react-test-renderer namespace.
People can import `react-shallow-renderer` as needed

## How did you test this change?

- Observe shallow.js in react-test-renderer package from standard build
- Run build with changes on this branch
- Observe no more shallow.js export in build output
2024-03-26 18:04:47 -04:00
Sebastian Markbåge 84c84d72f1 Remove enableClientRenderFallbackOnTextMismatch flag (#28458)
Build on top of #28440.

This lets us remove the path where updates are tracked on differences in
text.
2024-03-26 17:55:14 -04:00
Jack Pope f73d11f092 [RTR] Enable warning flag (#28419)
## Summary

Based on
- https://github.com/facebook/react/pull/27903

This PR
- Silence warning in React tests
- Turn on flag

We want to finish cleaning up internal RTR usage, but let's prioritize
the deprecation process. We do this by silencing the internal warning
for now.

## How did you test this change?

`yarn build`
`yarn test ReactHooksInspectionIntegration -b`
2024-03-26 17:44:31 -04:00
Sebastian Markbåge 670d61bea2 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.
2024-03-26 17:41:49 -04:00
Ricky dbfbfb3312 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.
2024-03-26 17:25:53 -04:00
Sebastian Markbåge f24cf4a1af 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.
2024-03-26 17:19:02 -04:00
Ricky 1a6d36b1a3 Remove unmountComponentAtNode outside of legacy mode (#28650) 2024-03-26 16:32:43 -04:00
Joe Savona 91f7bc8be7 Ensure valid mutable ranges for all scopes; fix ranges for context vars
Our current validation fails to detect some invalid cases of mutable ranges — 
namely, ranges that are fully or partially uninitialized, with start or 
start+end still set to zero. 

This PR fixes these cases, starting by validating that the ranges for _all_ 
reactive scopes are valid: start >= 1, and end <= (last instr id + 1). This 
exposed the invalid cases, which are also fixed here: 

* During AnalyzeFunctions, we need to reset identifier ranges and scopes when 
exiting an inner function. This has to happen *after* the effects have been 
translated to the function deps/context operands, in order for 
InferRefenceEffects to continue working on the outer function. Previously I did 
this at the start of InferReactiveScopeVariables, but that's insufficient bc the 
incorrect ranges could also influence InferMutableRanges. AnalyzeFunctions is 
the point at which we compute the ranges for identifiers in the inner function, 
so it's the most ideal place to clean those up so they don't influence the outer 
function. 

* In InferMutableLifetimes, we need to ensure that context variable identifiers 
end up with a mutable range starting where they are declared, and ending with 
their last assignment. We now track declarations and extend their mutable range 
to account for each reassignment.
2024-03-26 13:29:24 -07:00
Andrew Clark 56efb2e227 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`.
2024-03-26 15:31:57 -04:00
Jack Pope 738993da0b Turn on enableRenderableContext in experimental (#28645)
Let's get this into experimental to get more usage and allow other
renderers to test against the changes easier.
2024-03-26 14:17:43 -04:00
RickyandSébastien Lorber 0a44435674 Add useActionState to CHANGELOG-canary.md (#28632)
Co-authored-by: Sébastien Lorber <slorber@users.noreply.github.com>
2024-03-26 13:15:08 -04:00
Timothy Yung 6fd0cb9a9f 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
```
2024-03-26 10:01:38 -07:00
Jan Kassens 95319ab5af 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.
2024-03-26 12:13:46 -04:00
Ricky c3048aab4c Fix tests on main (#28643)
Not sure how these broke when merging
https://github.com/facebook/react/pull/28299 and
https://github.com/facebook/react/pull/28361
2024-03-26 11:26:42 -04:00
Ruslan Lesiutin 75de4ff72a fix[devtools/ci]: split profiling cache test for different react versions and toEqual checker (#28628)
This should fix the failing backwards-compatibility tests on CI:
-
https://app.circleci.com/pipelines/github/facebook/react/51347/workflows/9d319db5-7a29-4e9a-a3a0-8d49a24ee9bd/jobs/809381
-
https://app.circleci.com/pipelines/github/facebook/react/51347/workflows/9d319db5-7a29-4e9a-a3a0-8d49a24ee9bd/jobs/809386

Started failing after https://github.com/facebook/react/pull/27991.

Brief summary:
1. Revert changes to `profilingCache-test` in
https://github.com/facebook/react/pull/27991.
2. Scope previous test to react versions 16.9 - 18.2
3. Add a new test for react versions > 18.2 (includes testing react from
souce), which is also gated with `!disableLegacyContext`.

> [!IMPORTANT]
> `@gate` pragma expects the test to throw. Jest doesn't throw any
exceptions when snapshots are mismatched, this is why I've migrated test
from `toMatchInlineSnapshot` checker to `toEqual`.
> If the test doesn't throw, we will fail it manually:
>
https://github.com/facebook/react/blob/5a75f9e78544fa6d052aff7fe99607e48f35b979/scripts/jest/setupTests.js#L291-L295
2024-03-26 10:55:07 +00:00
Joe Savona fbd68eef7f Fix for invalid mutable range in phi with backedge
Fixes the repro added in 947832009997bf9149e88e583c46cc39f6a6136c - previously 
when computing mutable ranges of phis, we didn't check that all operands had 
been visited. This meant that a backedge could allow a phi's mutable range to 
start at 0. Then in PropagateScopeDeps, we might see reject dependencies of a 
scope since they appeared to start after a scope — only because the scope's 
start was incorrectly too early. 

The fix here is to initially set phi.id mutable ranges based on only on operands 
that are already visited. Then, during/after the fixpoint iteration of 
InferMutableRanges, we start account for all operands since we know they've been 
visited at least once and have a real range.
2024-03-25 21:36:33 -07:00
Ricky 07def0cc5a Add test for throttling suspended siblings (#28361)
# Overview

Adds a test to show the combination of the new throttling behavior and
not pre-rendering siblings results in pushing out content that could
have rendered much faster.

- Without the new throttling, the sibling content would render in 30ms.
- Without removing pre-rendering, the sibling content would render in
0ms.
- With both, the sibling content takes 600ms.

## Example


https://github.com/facebook/react/assets/2440089/abd62dc4-93f9-4b7b-a5aa-b795827c1a3a
2024-03-26 00:02:46 -04:00
Ricky 67e6fa6d17 Test for suspending with modern strict mode (#28513)
## Overview

Adds a test to show the cause of an infinite loop in Relay related to
[these effects in
Relay](https://github.com/facebook/relay/blob/448aa67d2a11e7d45cd7b4492b9f599b498cb39e/packages/react-relay/relay-hooks/useLazyLoadQueryNode.js#L77-L104)
and `useModernStrictMode`. The bug is related to effect behavior when
committing trees that re-suspend after initial mount.

With `useModernStrictEffect`, when you:
- initial mount
- update
- suspend (to fallbacks)
- resolve
- re-commit

We fire strict effects during the second mount, like it's a new tree.
This creates weird cases, where if there was an update while we
suspended, we'll first fire only the effects that changed dependencies,
and then fire strict effects.

Creating a test to demonstrate the behavior to see if it's a bug.
2024-03-26 00:02:16 -04:00
Ricky d3037405c4 Test showing mismatches after suspending force fallbacks to be shown (#28299)
While investigating https://github.com/facebook/react/issues/28285 I
found a possible bug in handling Suspense and mismatches. As the tests
show, if the first sibling in a boundary suspends, and the second has a
mismatch, we will NOT show a fallback. If the first sibling is a
mismatch, and the second sibling suspends, we WILL show a fallback.

[Here's a stackbliz showing the behavior on
Canary](https://stackblitz.com/edit/stackblitz-starters-bh3snf?file=src%2Fstyle.css,public%2Findex.html,src%2Findex.tsx).

This breakage was introduced by:
https://github.com/facebook/react/pull/26380. Before this PR, we would
not show a fallback in either case. That PR makes it so that we don't
pre-render siblings of suspended trees, so presumably, whatever
detection we had to avoid fallbacks on mismatches, requires knowing
there's a mismatch in the tree when we suspend.
2024-03-25 20:33:31 -04:00
Joe Savona 48ecaf92d5 Reset scopes from AnalyzeFunctions in InferReactiveScopeVariables
Updates InferReactiveScopeVariables to first prune scopes attached during 
AnalyzeFunctions. This ensures that after this pass the only scopes that exist 
on identifiers in the outer program are those that the pass explicitly inferred, 
and not accidentally leftover.
2024-03-25 15:31:23 -07:00
Joe Savona 29ab0f835e Repro for scopes from AnalyzeFunction influencing outer compilation
We share identifiers between outer functions and inner function expressions, 
which means that scopes inferred during AnalyzeFunctions can be retained on 
Identifiers in the outer function. If InferReactiveScopeVariables doesn't happen 
to visit an identifier we currently retain that scope information and use it 
when constructing scopes, even if there doesn't technically need to be a scope 
for that value. 

Fixed in the next PR.
2024-03-25 15:31:22 -07:00
Lauren Tan e6ce5e4922 [eslint] Update devDep to match types 2024-03-25 15:00:34 -04:00
Lauren Tan 15abd38911 Cleanup unused eslint-browser and js-fuzzer packages
These were never used, so let's clean them up
2024-03-25 15:00:31 -04:00
Jan Kassens 5a75f9e785 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.
2024-03-25 13:48:22 -04:00
dependabot[bot] 50cdf158d8 Bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /fixtures/flight (#28618)
Bumps
[webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware)
from 5.3.3 to 5.3.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/webpack/webpack-dev-middleware/releases">webpack-dev-middleware's
releases</a>.</em></p>
<blockquote>
<h2>v5.3.4</h2>
<h3><a
href="https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4">5.3.4</a>
(2024-03-20)</h3>
<h3>Bug Fixes</h3>
<ul>
<li><strong>security:</strong> do not allow to read files above (<a
href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1779">#1779</a>)
(<a
href="https://github.com/webpack/webpack-dev-middleware/commit/189c4ac7d2344ec132a4689e74dc837ec5be0132">189c4ac</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md">webpack-dev-middleware's
changelog</a>.</em></p>
<blockquote>
<h3><a
href="https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4">5.3.4</a>
(2024-03-20)</h3>
<h3>Bug Fixes</h3>
<ul>
<li><strong>security:</strong> do not allow to read files above (<a
href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1779">#1779</a>)
(<a
href="https://github.com/webpack/webpack-dev-middleware/commit/189c4ac7d2344ec132a4689e74dc837ec5be0132">189c4ac</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/webpack/webpack-dev-middleware/commit/86071ead69e946ada25497d3e281923e885229a4"><code>86071ea</code></a>
chore(release): 5.3.4</li>
<li><a
href="https://github.com/webpack/webpack-dev-middleware/commit/189c4ac7d2344ec132a4689e74dc837ec5be0132"><code>189c4ac</code></a>
fix(security): do not allow to read files above (<a
href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1779">#1779</a>)</li>
<li>See full diff in <a
href="https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=webpack-dev-middleware&package-manager=npm_and_yarn&previous-version=5.3.3&new-version=5.3.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/facebook/react/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 13:37:11 -04:00
dependabot[bot] b35be36fb4 Bump webpack-dev-middleware from 5.3.3 to 5.3.4 (#28621)
Bumps
[webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware)
from 5.3.3 to 5.3.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/webpack/webpack-dev-middleware/releases">webpack-dev-middleware's
releases</a>.</em></p>
<blockquote>
<h2>v5.3.4</h2>
<h3><a
href="https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4">5.3.4</a>
(2024-03-20)</h3>
<h3>Bug Fixes</h3>
<ul>
<li><strong>security:</strong> do not allow to read files above (<a
href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1779">#1779</a>)
(<a
href="https://github.com/webpack/webpack-dev-middleware/commit/189c4ac7d2344ec132a4689e74dc837ec5be0132">189c4ac</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md">webpack-dev-middleware's
changelog</a>.</em></p>
<blockquote>
<h3><a
href="https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4">5.3.4</a>
(2024-03-20)</h3>
<h3>Bug Fixes</h3>
<ul>
<li><strong>security:</strong> do not allow to read files above (<a
href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1779">#1779</a>)
(<a
href="https://github.com/webpack/webpack-dev-middleware/commit/189c4ac7d2344ec132a4689e74dc837ec5be0132">189c4ac</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/webpack/webpack-dev-middleware/commit/86071ead69e946ada25497d3e281923e885229a4"><code>86071ea</code></a>
chore(release): 5.3.4</li>
<li><a
href="https://github.com/webpack/webpack-dev-middleware/commit/189c4ac7d2344ec132a4689e74dc837ec5be0132"><code>189c4ac</code></a>
fix(security): do not allow to read files above (<a
href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1779">#1779</a>)</li>
<li>See full diff in <a
href="https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=webpack-dev-middleware&package-manager=npm_and_yarn&previous-version=5.3.3&new-version=5.3.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/facebook/react/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 13:36:03 -04:00
Jan Kassens 527ed72bfd Cleanup enableFormActions flag (#28614)
Cleanup enableFormActions flag
2024-03-25 13:25:14 -04:00
Sathya Gunasekaran 70302048a0 Update yarn.lock 2024-03-25 16:41:13 +00:00
Sebastian Silbermann e373190faf Guard against legacy context not being supported in DevTools fixture (#28596) 2024-03-25 14:30:08 +01:00
Ricky 0711ff1763 Add Type: Feature Request to stalebot ignore (#28625)
Prevent issues like https://github.com/facebook/react/issues/12525 from
closing due to inactivity.
2024-03-24 12:48:12 -04:00
Joe Savona 2ce112dbf2 Repro for missing dep with while/if using externally declared "index" variable
What happens here is that the phi node for `i` has its mutable range set to 
start at 0, because it has a back edge and we haven't initialized the mutable 
ranges of all its operands yet when we iterate the operands and set range.start 
= min(start of operand starts). 

Then the corresponding scope has its range set to start at 0 too. When 
PropagateScopeDeps runs it sees that `b` is from instruction 1, which is after 
the start of the scope (0), so it thinks `b` isn't a valid dependency. 

The fix is in InferMutableRanges, where we need to make sure that phis ignore 
their operand's ranges until those ranges are initialized.
2024-03-22 21:49:39 -07:00
Sathya Gunasekaran db7e7c7fae Support method call when validating useEffect
Treat MethodCall similarly to CallExpression when validation useEffect
2024-03-22 23:42:27 +00:00
Lauren Tan 1f45b13fda Update yarn.lock 2024-03-22 18:59:44 -04:00
Joe Savona 200d2c379d Fix for loops with value block index initial value 2024-03-22 14:51:36 -07:00
Joe Savona f4ff1a28e7 Fix for destructuring with partial context variables 2024-03-22 14:32:50 -07:00
Joe Savona 73fa712108 Repro for destructuring with partial context variables 2024-03-22 14:32:47 -07:00
Jan Kassens d7d82e7bee Correct eslint-plugin-react-compiler dependencies
Correct eslint-plugin-react-compiler dependencies 

- The eslint plugin doesn't actually depend on the babel plugin as it compiles 
in the dependencies. - `zod` and `zod-validation-error` were missing, but 
required to run the plugin. - Update to the `hermes-parser` dependency just to 
keep it updated.
2024-03-22 17:03:30 -04:00
Joe Savona 4575c8a5f6 Detect hoisting where the reference is a reassignment
Our logic to detect hoisting relies on Babel's `isReferencedIdentifier()` to 
determine whether a reference to an identifier is a reference or a declaration. 
The idea is that we want to find references to variables that may be hoistable, 
before the declaration — the definition of hoisting. But due to the bug in 
isReferencedIdentifier, we skipped over reassignments of hoisted variables. The 
hack here checks if an identifier is a direct child of an AssignmentExpression, 
ensuring we visit reassignments.
2024-03-22 13:49:50 -07:00
Joe Savona fb0f7e5c39 Repro for closures reassigning hoisted let variables
Adds examples for closures that reassign hoisted let bindings. We currently 
compile these as context variables without hoisting, so we hit an invariant in 
InferReferenceEffects when the variable is referenced before being declared.
2024-03-22 11:53:29 -07:00
Joe Savona e7d0c81d13 Add todo for for loops with context iterator variable
Detect the previous case — for loops where the iterator is a context variables — 
and throw a todo rather than hitting the invariant.
2024-03-22 11:46:28 -07:00
Jan Kassens f09e1599d6 Fix test after merge conflict (#28616)
Something went wrong when rebasing #28491  and renaming the hook.
2024-03-22 14:05:55 -04:00
Ricky 5c65b27587 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.
2024-03-22 13:03:44 -04:00
Ricky fabd6d3a64 Better NEXT_MAJOR support for RN flags (#28583)
The `__NEXT_MAJOR__` value in the RN flags doesn't make sense because:

a) The flags are for the next RN major, since it only impacts the
renderers
b) The flags are off, so they're not currently in the next major, they
need enabled
c) the flag script didn't support it

This PR adds two aliases to the RN file:
- `__TODO_NEXT_RN_MAJOR__`: flags that need enabled before the next RN
major.
- `__NEXT_RN_MAJOR__`: flags that have been enabled since the last RN
major.

These values will need to be manually kept up to date when we cut a RN
version, but once RN switches to the canary build and aligns all the
flags, this entire file can be deleted.

## Script screen
Notably, I added a TODO value and a legend that prints at the end of the
script:

<img width="1078" alt="Screenshot 2024-03-18 at 8 11 27 PM"
src="https://github.com/facebook/react/assets/2440089/14da9066-f77d-437f-8188-830a31a843c5">
2024-03-22 13:02:04 -04:00
Ricky fa0efa1ae3 Update RN dynamic flag types (#28427)
Updates the RN flag flow types to work like www does, so we can use the
`.native-fb-dynamic.js` file as the type/shim for the dynamically
imported file.
2024-03-22 12:23:38 -04:00
Jan Kassens 208ceeb46c Cleanup enableFloat flag (#28613)
Cleanup enableFloat flag
2024-03-22 12:22:30 -04:00
Joe Savona bc516409a6 Repro of for loop with context variable iterator variable
`let` bindings of context variables are lowered to a DeclareContext + 
StoreContext, which breaks codegen for `for` loops which expect that all 
statements of the init block will lower to variable declarations. The two 
instructions produce a variable declaration and a reassignment.
2024-03-22 09:15:29 -07:00
Joe Savona 7dc08d79be PropagateScopeDeps treats switch w only default as unconditional
If we have a switch with only a default case, then that code will be executed 
unconditionally. PropagateScopeDeps can take advantage of this to record 
dependencies in these cases as unconditional, which avoids the issue seen in the 
previous PR.
2024-03-22 09:02:56 -07:00
Joe Savona 3229302222 Repro for "expect trees to be >=2 elements long" invariant
These cases have a switch with only a default, so we create a "conditional" 
child dependency tree and no other trees which hits the invariant.
2024-03-22 08:53:30 -07:00