This rule was a leftover from a while ago and doesn't actually lint anything useful. Specifically, you get a lint error if you try to opt out a component that isn't already bailing out. If there's a bailout the compiler already safely skips over it, so adding `'use no memo'` there is unnecessary.
Fixes#31407
Previously, the `recommended` config used the legacy ESLint format (plugins as an array of strings). This causes errors when used with ESLint v9's `defineConfig()` helper. This was following [eslint's own docs](https://eslint.org/docs/latest/extend/plugins#backwards-compatibility-for-legacy-configs):
> With this approach, both configuration systems recognize "recommended". The old config system uses the recommended key while the current config system uses the flat/recommended key. The defineConfig() helper first looks at the recommended key, and if that is not in the correct format, it looks for the flat/recommended key. This allows you an upgrade path if you’d later like to rename flat/recommended to recommended when you no longer need to support the old config system.
However, [`isLegacyConfig()`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L73-L81) (also see [`eslintrcKeys`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L24-L35)) function doesn't check for the `plugins` key, so our config was incorrectly treated as flat config despite being in legacy format.
This PR fixes the issue, along with a few other fixes combined:
1. Convert `recommended` to flat config format
2. Separate basic rules (exhaustive-deps, rules-of-hooks) from compiler rules
3. Add `recommended-latest-legacy` config for non-flat config users who want all recommended rules (including compiler rules)
4. Adding more types for the exported config
Our shipped presets in 6.x.x will essentially be:
- `recommended-legacy`: legacy (non-flat), with basic rules only
- `recommended-latest-legacy`: legacy (non-flat), all rules (basic + compiler)
- `flat/recommended`: flat, basic rules only (now the same as recommended, but to avoid making a breaking change we'll just keep it around in 6.x.x)
- `recommended-latest`: flat, all rules (basic + compiler)
- `recommended`: flat, basic rules only
In the next breaking release 7.x.x, we will collapse down the presets into three:
- `recommended-legacy`: all recommended rules
- `recommended`: all recommended rules
- `recommended-experimental`: all recommended rules + new bleeding edge experimental rules
Closes#34679
This rule was a leftover from a while ago and doesn't actually lint anything useful. Specifically, you get a lint error if you try to opt out a component that isn't already bailing out. If there's a bailout the compiler already safely skips over it, so adding `'use no memo'` there is unnecessary.
Fixes#31407
This rule was a leftover from a while ago and doesn't actually lint anything useful. Specifically, you get a lint error if you try to opt out a component that isn't already bailing out. If there's a bailout the compiler already safely skips over it, so adding `'use no memo'` there is unnecessary.
Fixes#31407
Previously, the `recommended` config used the legacy ESLint format (plugins as an array of strings). This causes errors when used with ESLint v9's `defineConfig()` helper. This was following [eslint's own docs](https://eslint.org/docs/latest/extend/plugins#backwards-compatibility-for-legacy-configs):
> With this approach, both configuration systems recognize "recommended". The old config system uses the recommended key while the current config system uses the flat/recommended key. The defineConfig() helper first looks at the recommended key, and if that is not in the correct format, it looks for the flat/recommended key. This allows you an upgrade path if you’d later like to rename flat/recommended to recommended when you no longer need to support the old config system.
However, [`isLegacyConfig()`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L73-L81) (also see [`eslintrcKeys`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L24-L35)) function doesn't check for the `plugins` key, so our config was incorrectly treated as flat config despite being in legacy format.
This PR fixes the issue, along with a few other fixes combined:
1. Convert `recommended` to flat config format
2. Separate basic rules (exhaustive-deps, rules-of-hooks) from compiler rules
3. Add `recommended-latest-legacy` config for non-flat config users who want all recommended rules (including compiler rules)
4. Adding more types for the exported config
Our shipped presets in 6.x.x will essentially be:
- `recommended-legacy`: legacy (non-flat), with basic rules only
- `recommended-latest-legacy`: legacy (non-flat), all rules (basic + compiler)
- `flat/recommended`: flat, basic rules only (now the same as recommended, but to avoid making a breaking change we'll just keep it around in 6.x.x)
- `recommended-latest`: flat, all rules (basic + compiler)
- `recommended`: flat, basic rules only
In the next breaking release 7.x.x, we will collapse down the presets into three:
- `recommended-legacy`: all recommended rules
- `recommended`: all recommended rules
- `recommended-experimental`: all recommended rules + new bleeding edge experimental rules
Closes#34679
Previously, the `recommended` config used the legacy ESLint format (plugins as an array of strings). This causes errors when used with ESLint v9's `defineConfig()` helper. This was following [eslint's own docs](https://eslint.org/docs/latest/extend/plugins#backwards-compatibility-for-legacy-configs):
> With this approach, both configuration systems recognize "recommended". The old config system uses the recommended key while the current config system uses the flat/recommended key. The defineConfig() helper first looks at the recommended key, and if that is not in the correct format, it looks for the flat/recommended key. This allows you an upgrade path if you’d later like to rename flat/recommended to recommended when you no longer need to support the old config system.
However, [`isLegacyConfig()`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L73-L81) (also see [`eslintrcKeys`](https://github.com/eslint/rewrite/blob/main/packages/config-helpers/src/define-config.js#L24-L35)) function doesn't check for the `plugins` key, so our config was incorrectly treated as flat config despite being in legacy format.
This PR fixes the issue, along with a few other fixes combined:
1. Convert `recommended` to flat config format
2. Separate basic rules (exhaustive-deps, rules-of-hooks) from compiler rules
3. Add `recommended-latest-legacy` config for non-flat config users who want all recommended rules (including compiler rules)
4. Adding more types for the exported config
Our shipped presets will essentially be:
- `recommended-legacy`: legacy (non-flat), with basic rules only
- `recommended-latest-legacy`: legacy (non-flat), all rules (basic + compiler)
- `flat/recommended`: flat, basic rules only
- `recommended-latest`: flat, all rules (basic + compiler)
- `recommended`: flat, basic rules only
In a future breaking release, we will collapse down the presets into three:
- `recommended-legacy`: all recommended rules
- `recommended`: all recommended rules
- `recommended-experimental`: all recommended rules + new bleeding edge experimental rules
Closes#34679
This auto updates to select the last entry in the timeline until we make
the first selection. That way when new content loads in, we show the
last timeline of what is visible.
When we flush a Suspense boundary we might not flush the fallback
segment, it might only flush a placeholder instead. In this case the
segment can flush again but we do not want to flush the boundary itself
a second time. We now detach the boundary after flushing it.
better solution to: https://github.com/facebook/react/pull/34668
We're showing too much noise in the side-panel when selecting a Suspense
boundary. The interesting thing to see directly is the "Suspended by".
The "props" are mostly useless because the `"name"` prop is already in
the tree. I'm now also showing it in the title bar of the selected
element panel. The "children" and "fallback" props are just the thing
that you can see in the tree view anyway.
The "state" is this weird section with just one field in it, which we
already have duplicated in the top toolbar as well. We can just delete
this. I make sure to show the icon and a "suspended..." section while
the boundary is still loading but now yet resuspended by force
suspending.
While still loading:
<img width="600" height="193" alt="Screenshot 2025-09-27 at 11 54 37 PM"
src="https://github.com/user-attachments/assets/1c3f3a96-46e0-4b11-806f-032569c7d5b5"
/>
After loading:
<img width="602" height="266" alt="Screenshot 2025-09-27 at 11 54 53 PM"
src="https://github.com/user-attachments/assets/c43cc4cb-036f-4ced-9b0d-226c6320cd76"
/>
Resuspended after loading:
<img width="602" height="300" alt="Screenshot 2025-09-27 at 11 55 07 PM"
src="https://github.com/user-attachments/assets/0be01735-48a7-47dc-b5cf-e72ec71e0148"
/>
Rebased on #34454.
Always include the root in the timeline even if it has no unique
suspenders, since even if it won't suspend, we have to be able to see
that and step to one step before the next boundary to see the first
boundary that does suspend in its fallback state.
Also, if there's no current selection on initial mount, select the last
entry in the timeline. We usually do this with `selectedSuspenseID` but
that doesn't happen on initial load. So this does it on initial load if
nothing else is selected by then. That way when you reload you get the
initial root selected.
There's a problem here because we should really use one source of truth
and `selectedSuspenseID` doesn't really do anything now. Either it
should be its separate source of truth and you can't show components in
the side-panel or it should be derived from the other state.
If it's derived, once there's a selection, e.g. in the root, then even
if new timelines load it will never change but that's probably a good
thing.
This enables `@enablePreserveExistingMemoizationGuarantees` by default.
As of the previous PR (#34503), this mode now enables the following
behaviors:
- Treating variables referenced within a `useMemo()` or `useCallback()`
as "frozen" (immutable) as of the start of the call. Ie, the compiler
will assume that the values you reference are not mutated by the body of
the useMemo, not are they mutated later. Directly modifying them (eg
`var.property = true`) will be an error.
- Similarly, the results of the useMemo/useCallback are treated as
frozen (immutable) after the call.
These two rules match the behavior for other hooks: this means that
developers will see similar behavior to swapping out `useMemo()` for a
custom `useMyMemo()` wrapper/alias.
Additionally, as of #34503 the compiler uses information from the manual
dependencies to know which variables are non-nullable. Even if a useMemo
block conditionally accesses a nested property — `if (cond) { log(x.y.z)
}` — where the compiler would not usually know that `x` is non-nullable,
if the user specifies `x.y.z` as a manual dependency then the compiler
knows that `x` and `x.y` are non-nullable and can infer a more precise
dependency.
Finally, this mode also ensures that we always memoize function calls
that return primitives. See #34343 for more details.
For now, I've explicitly opted out of this feature in all test fixtures
where the behavior changed.
The `@enablePreserveExistingMemoizationGuarantees` mode can still fail
to preserve manual memoization due to mismtached dependencies.
Specifically, where the user's dependencies are more precise than the
compiler infers bc the compiler is being conservative about what might
be nullable. In this mode though we're intentionally using information
from the manual memoization and can also rely on the deps as a signal
for what's non-nullable.
The idea of the PR is that we treat manual memo deps just like other
inferred-as-non-nullable objects during PropagateScopeDeps. We're
careful to not treat the full path as non-nullable, only up to the last
property index. So `x.y.z` as a manual dep treats `x` and `x.y` as
non-nullable, allowing us to preserve a conditional dependency on
`x.y.z`.
Optionals within manual dependencies are a bit trickier and aren't
handled yet, but hopefully that's less common and something we can
improve in a follow-up. Not handling them just means that developers may
hit false positives on validating existing memoization if they use
optional chains in manual dependencies.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34503).
* #34689
* __->__ #34503
The View Transition docs were unclear about this but apparently the
`finished` promise never settles if the animation never started. So if
there's an error that rejects the `ready` promise, we'll never run the
clean up which can cause it to stall.
Fixes#34662.
However, ultimately that is caused by Chrome stalling our default
`onDefaultTransitionIndicator` but it should be unblocked after 10
seconds, not a minute.
Follow up to #34649. This adds the compiler rules back so they can be
opted-in 6.1.0, but aren't included in the presets as that would be a
breaking change.
Follow up to #34649. This adds the compiler rules back so they can be opted-in 6.1.0, but aren't included in the presets as that would be a breaking change.
Called Before:
> `logEvent` is a function created with React Hook "useEffectEvent", and
can only be called from the same component.
Called After:
> `logEvent` is a function created with React Hook "useEffectEvent", and
can only be called from Effects and Effect Events in the same component.
Referenced Before:
> `logEvent` is a function created with React Hook "useEffectEvent", and
can only be called from the same component. They cannot be assigned to
variables or passed down.
Referenced After:
> `logEvent` is a function created with React Hook "useEffectEvent", and
can only be called from Effects and Effect Events in the same component.
It cannot be assigned to a variable or passed down.
Reset EventTime when clearing timers. We need to track repeat updates
separately.
Typically we always reset all timers when we've logged an update. The
same update shouldn't be logged again.
I was trying to be clever and not reset the XEventTime because we also
need the timestamp to know if it's a repeat event. However, because of
this it looked like we had an event schedule an update even after we had
reset them.
This always resets the XEventTime to -1.1 and then stashes the old time
on EventRepeatTime which is our indication whether the next update was a
repeat of the old event.
---------
Co-authored-by: Ruslan Lesiutin <28902667+hoxyq@users.noreply.github.com>
Co-authored-by: Ricky <rickhanlonii@gmail.com>