Commit Graph
1043 Commits
Author SHA1 Message Date
Jiwon ChoiandGitHub 4950b5b789 [DevTools] Add ignore-listed stack frame disclosure (#36828) 2026-06-25 15:57:28 +02:00
uditandGitHub bb3548eff8 [react-devtools] Fix trailing percent sign in formatConsoleArguments (#36852)
## Summary

`formatConsoleArguments` (used by the DevTools backend to inline console
substitutions) walks the format string and inlines `%s`/`%d`/`%i`/`%f`
arguments while leaving `%c`/`%o`/`%O` in place. For each `%` it reads
the **next** character to decide what to do.

When the format string ends with a lone `%` — e.g.
`console.log('Progress 100%', value)` — the character after `%` is
`undefined`, and the `default` branch ran:

```js
template += `%${nextChar}`; // -> "%undefined"
```

So the function emitted the literal text `%undefined`:

```js
formatConsoleArguments('Progress 100%', 'extra');
// before: ['Progress 100%undefined', 'extra']
// after:  ['Progress 100%', 'extra']
```

Browsers render a trailing `%` in a console format string as a literal
percent sign, so this PR keeps it as `%` when there is no following
character.

## How did you test this change?

Added a `keeps a trailing percent sign` test to the existing
`formatConsoleArguments` suite in `utils-test.js` (covering both a bare
trailing `%` and one that follows another substitution). Since the
function is pure and import-free, I also verified the patched logic
against every existing case in that suite plus the new ones to confirm
there are no regressions.
2026-06-22 21:29:09 +01:00
Ruslan LesiutinandGitHub 15794a4907 [DevTools] Extract getDispatcherRef and fork inspectHooks() to avoid pulling React into the build artifact (#36681)
This is just a refactor to unblock the work on the Facade. This PR
contains 2 changes:
1. `getDispatcherRef` was extracted from `fiber/renderer` into its own
shared module.
2. `inspectHooksOfFiber` was forked. The new version specifies
dispatcher as required and doesn't set it to default as
`ReactSharedInternals`, which are pulling the whole `react` module as
part of them:


https://github.com/facebook/react/blob/3f6785f59e8871e232e94276db82b53299a36bb3/packages/shared/ReactSharedInternals.js#L10-L15
2026-06-15 08:27:11 +01:00
Sam ZhouandGitHub fa0542dc66 [flow] Update files synced to react-native to use modern flow syntax (#36759)
## Summary

This diff bumps a few hermes-parser related dependencies so that it can
consume modern Flow syntax. In addition, it makes targeted changes to
two files that will be synced to react-native so that we can enforce we
only use modern flow syntax in react-native.

## How did you test this change?

flow
2026-06-12 12:56:15 -04:00
Sam ZhouandGitHub 4cc753e62f [flow] Bump flow to v0.317.0 (#36701)
## Summary

Mostly changing the casting syntax from `(x: Y)` to `x as Y`, as the old
syntax was deprecated and always causing an error in Flow

## How did you test this change?

`yarn flow-ci`
2026-06-05 19:17:18 -04:00
Sam ZhouandGitHub 78cf315a48 [flow] Bump flow to v0.307.1 (#36199)
## Summary

Notable changes:
- Only $FlowFixMe, $FlowExpectedError suppression comments are supported
- All suppression comments need error code
- A lot of new invalid-compare and constant-condition errors suppressed.
These errors reveal potential logical bugs

## How did you test this change?

`yarn flow-ci`
2026-06-05 13:54:38 -04:00
Ruslan LesiutinandGitHub e72dc6f733 fix[describeClassComponentFrame]: invoke constructor with new keyword (#36455)
For JavaScript runtimes that do not have
[`Reflect`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect)
supported, we had a fall back that was calling the constructor with
overridden `this` context via
```
fn.apply(Fake.prototype);
```

In ES6, it is required to call constructor only with the `new` keyword,
otherwise the runtime is expected to throw a corresponding TypeError:
```
TypeError: Class constructor <> cannot be invoked without 'new'
```

We've observed this error in Hermes runtime, but this is applicable to
V8 or any other runtime. The only reason why V8 wasn't affected is
because it implemented Reflect APIs.

Instead of the incorrect call, we will fall back to calling `new fn()`,
but with a temporary patched prototype of the class, which would make a
trap out of the setter for `props` object. We use the same approach when
`Reflect` APIs are available, but instead of modifying the prototype, we
pass the fake context:

https://github.com/facebook/react/blob/878b6fb60ce5a2347ec1bfa0741edf0b2d08f157/packages/shared/ReactComponentStackFrame.js#L129-L148


---

See tests implemented. Without the changes, the test would fail with the
`TypeError` mentioned above.
2026-05-13 22:05:14 +01:00
zxuhan7andGitHub 087d057db4 [DevTools] Preserve -Infinity in inspected values (#36347)
## Summary

`getDataType` collapsed both `Infinity` and `-Infinity` to the
`'infinity'` data type, so a `-Infinity` value coming from inspected
props/state/hooks was rehydrated on the frontend as `Infinity`.

This adds a `'-infinity'` `DataType`, routes it through
`dehydrate`/`hydrate` alongside the existing `'infinity'` arm, and makes
`smartParse`/`smartStringify` (used for editable hook values) symmetric.

## Files

- `packages/react-devtools-shared/src/utils.js` — extend `DataType`,
split sign in `getDataType`, route `'-infinity'` through
`formatDataForPreview`.
- `packages/react-devtools-shared/src/hydration.js` — `dehydrate` and
`hydrate` cases for `'-infinity'`.
- `packages/react-devtools-shared/src/devtools/utils.js` — `smartParse`
accepts `'-Infinity'`; `smartStringify` returns `'-Infinity'` for
negative infinite values.
-
`packages/react-devtools-shared/src/__tests__/inspectedElement-test.js`
and `legacy/inspectElement-test.js` — added `minus_infinity={-Infinity}`
to the simple-data-types tests + snapshots.
-
`packages/react-devtools-shell/src/app/InspectableElements/SimpleValues.js`
— added `minusInfinity` to the dev shell so the path is exercised
manually.

## Test plan

- [x] `yarn prettier` / `yarn linc`
- [x] `yarn flow dom-node` — no errors
- [x] `yarn test --silent --no-watchman -t "should support simple data
types"` (source channel)
- [x] `yarn test-www --silent --no-watchman -t "should support simple
data types"` (www-modern)
- [ ] `yarn test-build-devtools` — relies on a built bundle; left to CI
per repo policy.

Fixes #32552
2026-05-02 09:50:40 +01:00
petertdinhandGitHub 72e06b7129 fix(devtools): clear highlight when mouse leaves DevTools panel (#36177)
Fixes #17855

When hovering a component in the DevTools Components inspector, a
highlight overlay appears on the inspected page. The highlight is
cleared via `onMouseLeave` on the tree container `div`. But this React
synthetic event only fires when the pointer transitions between elements
_within the same document_. When the user moves their mouse out of the
DevTools panel window entirely (e.g. to the browser viewport), no
element in the React tree receives `mouseleave`, so
`clearHostInstanceHighlight` is never sent over the bridge and the
overlay persists on the page.

The fix adds a native `mouseleave` listener on the DevTools panel's
`ownerDocument` in `Tree.js`. When the pointer exits the panel viewport,
it fires `clearHighlightHostInstance` and removes the overlay. Using
`ownerDocument` (rather than document) is consistent with the existing
pattern in `Tree.js` for browser extension compatibility.

How did you test this change?

Tested manually using the Chrome extension:

1. Opened React DevTools → Components tab on a React app
2. Hovered a component in the tree — highlight appeared on the page ✓
3. Moved the mouse out of the DevTools panel into the browser viewport —
highlight cleared immediately ✓ (previously it persisted)
4. Moved the mouse back into the panel and hovered a component —
highlighting still works normally ✓
5. Unhovered within the panel — highlight still clears correctly ✓

Ran the DevTools test suite: yarn test --no-watchman ReactDevTools — all
tests pass.
2026-05-02 09:50:26 +01:00
Sebastian "Sebbie" SilbermannandGitHub d3449f6200 [DevTools] Remove spurious warnings when creating Suspense timeline from a Transition (#36133) 2026-04-08 18:49:35 +02:00
Bodhi Russell SilberlingandGitHub 2cf6d3e757 Fix typos: occured->occurred, teh->the, accomodate->accommodate (#35616)
Fixed spelling errors in comments and error messages:
- Fixed 'occured' -> 'occurred' in ReactAsyncActions-test.js
- Fixed 'teh' -> 'the' in ReactFiberConfigDOM.js
- Fixed 'occured' -> 'occurred' in ErrorBoundary.js
- Fixed 'accomodate' -> 'accommodate' in InferMutationAliasingEffects.ts

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
2026-03-31 16:01:58 -07:00
Ruslan LesiutinandGitHub e70ba593ac [DevTools] feat: display subtree for Activity and dim in hidden mode (#36094)
With this change, Components panel will display subtree of the Activity.
When it is in hidden mode, the subtree will be dimmed.

Added Jest tests and a sandbox case to `react-devtools-shell`.

Demo:


https://github.com/user-attachments/assets/69a2e8d6-585d-4fcd-b57e-e9ae06d0a1b3
2026-03-23 14:29:38 +00:00
Ruslan LesiutinandGitHub 6844e8011f [DevTools] chore: extract getInternalReactConstants (#35986)
The test is quite naive, but worth having as additional check for
changes in this logic.
2026-03-20 14:17:15 +00:00
Ruslan LesiutinandGitHub aba4f90381 [DevTools] fix: don't show empty suspended by section (#36011)
The potential paddings and margins for the empty block are already
handled via CSS selectors.
2026-03-12 14:19:21 -07:00
Sebastian "Sebbie" SilbermannandGitHub 0caec283d6 [DevTools] Ignore new production renderers if we already use "worse" versions of React on a page (#35994) 2026-03-11 10:26:35 +01:00
Sebastian "Sebbie" SilbermannandGitHub b4d0874823 [DevTools] Fix a crash when rendering a new class Component when simulating errored state (#35985) 2026-03-10 18:05:48 +01:00
932543873d [DevTools] Fix null ref crash in ContextMenu when items list is empty (#35929)
Co-authored-by: Sebastian Sebbie Silbermann <[email protected]>
2026-03-05 16:52:23 +01:00
Ruslan LesiutinandGitHub 8aa3683da6 [DevTools] chore: extract pure functions from fiber/renderer.js (#35924)
I am in a process of splitting down the renderer implementation into
smaller units of logic that can be reused. This change is about
extracting pure functions only.
2026-03-03 12:27:05 +00:00
Sebastian "Sebbie" SilbermannandGitHub ad10f7c20d [DevTools] Only schedule a single update per Supense when changing timeline (#35927) 2026-03-03 12:21:46 +01:00
Sebastian "Sebbie" SilbermannandGitHub 8d395dce04 [DevTools] Fix alignment of breadcrumbs separator (#35817) 2026-02-18 10:53:49 -08:00
Ruslan LesiutinandGitHub a1077c4596 [DevTools] Check suspense child node presence in parentSuspense.children before removing (#35775)
Currently, this silently removes the last child in the list, which
doesn't contain the `id`.
2026-02-13 15:33:22 +00:00
Sebastian "Sebbie" SilbermannandGitHub 9e1fb1aab8 [DevTools] Dedicated empty state for roots that aren't suspended by anything (#35769) 2026-02-12 17:48:02 +01:00
Sebastian "Sebbie" SilbermannandGitHub f60a406297 [DevTools] Enable Suspense tab by default (#35768) 2026-02-12 16:50:29 +01:00
Ruslan LesiutinandGitHub a3c56f2a29 [DevTools] Remove experimental __IS_INTERNAL_MCP_BUILD__ flag and related code (#35755)
This is unused.
2026-02-11 16:59:43 +00:00
Sebastian "Sebbie" SilbermannandGitHub 6e94f65dfa [DevTools] Display React.optimisticKey in key positions (#35760) 2026-02-11 00:35:36 +01:00
Sebastian "Sebbie" SilbermannandGitHub 2bb178edeb [DevTools] Only block child Suspense boundaries if the parent has all shared suspenders removed (#35737) 2026-02-10 17:52:35 +01:00
d0206107b3 Consistent handling of work tags for rename, delete, and overriding state (#35740)
Co-authored-by: Ruslan Lesiutin <[email protected]>
2026-02-10 16:35:02 +01:00
Sebastian "Sebbie" SilbermannandGitHub 75593946c9 [DevTools] Fix memory leak when unmounting hoistables (#35741) 2026-02-10 13:09:23 +01:00
Sebastian "Sebbie" SilbermannandGitHub c6983ae9a2 [test] Include uniqueSuspenders in Suspense tree snapshots (#35736) 2026-02-10 12:51:58 +01:00
Ruslan LesiutinandGitHub 4210e49bb3 [DevTools] Don't capture durations for disconnected subtrees when profiling (#35718)
After https://github.com/facebook/react/pull/34089, when updating
(possibly, mounting) inside disconnected subtree, we don't record this
as an operation. This only happens during reconnect. The issue is that
`recordProfilingDurations()` can be called, which diffs tree base
duration and reports it to the Frontend:

https://github.com/facebook/react/blob/cba2bc83d8ce7887fabb90775d25b67ade36e5c3/packages/react-devtools-shared/src/backend/fiber/renderer.js#L4506-L4521

This operation can be recorded before the "Add" operation, and it will
not be resolved properly on the Frontend side.

Before the fix:
```
commit tree › Suspense › should handle transitioning from fallback back to content during profiling

    Could not clone the node: commit tree does not contain fiber "5". This is a bug in React DevTools.

      162 |     const existingNode = nodes.get(id);
      163 |     if (existingNode == null) {
    > 164 |       throw new Error(
          |             ^
      165 |         `Could not clone the node: commit tree does not contain fiber "${id}". This is a bug in React DevTools.`,
      166 |       );
      167 |     }

      at getClonedNode (packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:164:13)
      at updateTree (packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:348:24)
      at getCommitTree (packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js:112:20)
      at ProfilingCache.getCommitTree (packages/react-devtools-shared/src/devtools/ProfilingCache.js:40:46)
      at Object.<anonymous> (packages/react-devtools-shared/src/__tests__/profilingCommitTreeBuilder-test.js:257:44)
```
2026-02-09 22:18:17 +00:00
Sebastian "Sebbie" SilbermannandGitHub c31165650f [DevTools] Allow renaming Host Component props (#35735) 2026-02-09 23:14:46 +01:00
Sebastian "Sebbie" SilbermannandGitHub e33fa11c51 [DevTools] Fix crash when revealing stable, filtered <Activity> children (#35734) 2026-02-09 22:52:24 +01:00
Ruslan LesiutinandGitHub 4d6a84745e [DevTools] Fix false-positive re-render reports for filtered nodes (#35723)
Fixes https://github.com/facebook/react/issues/33423,
https://github.com/facebook/react/issues/35245,
https://github.com/facebook/react/issues/19732.

As demoed
[here](https://github.com/facebook/react/issues/33423#issuecomment-2970750588),
React DevTools incorrectly highlights re-renders for descendants of
filtered-out nodes that didn't actually render.

There were multiple fixes suggesting changes in `didFiberRender()`
function, but these doesn't seem right, because this function is used in
a context of whether the Fiber actually rendered something (updated),
not re-rendered compared to the previous Fiber.

Instead, this PR adds additional validation at callsites that either
used for highlighting re-renders or capturing tree base durations and
are relying on `didFiberRender`. I've also added a few tests that
reproduce the failure scenario. Without the changes, the tests are
failing.
2026-02-09 20:39:33 +00:00
Sebastian "Sebbie" SilbermannandGitHub 048e4a3e1a [Fiber] Avoid duplicate debug info for array children (#35733) 2026-02-09 20:36:56 +01:00
Sebastian "Sebbie" SilbermannandGitHub cba2bc83d8 [test] Move profilingCommitTreeBuilder to versioned renderer (#35711) 2026-02-06 16:00:43 +01:00
Sebastian "Sebbie" SilbermannandGitHub f0425d4279 [DevTools] Fix broken commit tree builder for initial operations (#35710) 2026-02-06 15:16:23 +01:00
Sebastian "Sebbie" SilbermannandGitHub 514d8140c3 [DevTools] Separate breadcrumbs with » (#35705) 2026-02-06 00:40:31 +01:00
Sebastian "Sebbie" SilbermannandGitHub 50a8fab165 [DevTools] Avoid scrollbars in Suspense breadcrumbs (#35700) 2026-02-05 23:27:57 +01:00
Ruslan LesiutinandGitHub bf70c37a31 [DevTools] Throw an error when attempting to clone non-existent node (#35702)
There is an existing issue with serialisation logic for the traces from
Profiler panel.

I've discovered that `TREE_OPERATION_UPDATE_TREE_BASE_DURATION`
operation for some reason appears earlier in a sequence of operations,
before the `TREE_OPERATION_ADD` that registers the new node. It ends up
cloning non-existent node, which just creates an empty object and adds
it to the map of nodes.

This change only adds additional layer of validation to cloning logic,
so we don't swallow the error, if we attempt to clone non-existent node.
2026-02-05 17:49:18 +00:00
Sebastian "Sebbie" SilbermannandGitHub ae4dd460f5 [DevTools] Shrink/Deshrink Owners breadcrumbs on any resizes (#35694) 2026-02-05 12:08:02 +01:00
Sebastian "Sebbie" SilbermannandGitHub 6684f5a42e [DevTools] Disable Activity slices by default (#35685) 2026-02-04 10:56:33 +01:00
Ruslan LesiutinandGitHub 2647358088 [DevTools] Fix commit index reset when switching profiler roots (#35672)
Fixes https://github.com/facebook/react/issues/31463,
https://github.com/facebook/react/issues/30114.

When switching between roots in the profiler flamegraph, the commit
index was preserved from the previous root. This caused an error
"Invalid commit X. There are only Y commits." when the new root had
fewer commits than the selected index.

This fix resets the commit index to 0 (or null if no commits) when the
commitData changes, which happens when switching roots.
2026-02-03 12:44:04 +00:00
Sebastian "Sebbie" SilbermannandGitHub f49e0a25cb [DevTools] Apply component filters on initial load (#35587) 2026-01-26 11:06:04 +01:00
Sebastian "Sebbie" SilbermannandGitHub b1d37915c7 [DevTools] Update inspected element on component filter changes (#35599) 2026-01-26 11:04:06 +01:00
Sebastian "Sebbie" SilbermannandGitHub 4337f132b1 [DevTools] Stop setting unused global variables (#35532) 2026-01-16 16:13:29 +01:00
Sebastian "Sebbie" SilbermannandGitHub 0be64314a5 [DevTools] Clear element inspection if host element not owned by any renderer is selected (#35504) 2026-01-16 13:20:44 +01:00
Sebastian "Sebbie" SilbermannandGitHub 919828c090 Typecheck React DevTools extension main script (#35519) 2026-01-16 13:08:28 +01:00
Sebastian "Sebbie" SilbermannandGitHub c33cde8480 [DevTools] Show fallback in inspected element pane when no element is selected (#35503) 2026-01-15 14:28:02 +01:00
Sebastian "Sebbie" SilbermannandGitHub d2df88809e [DevTools] Add React Element pane to browser Elements panel (#35240) 2026-01-15 13:24:06 +01:00
48f97abf95 Improve the detection of changed hooks (#35123)
## Summary

cc @hoxyq 

Fixes https://github.com/facebook/react/issues/28584. Follow up to PR:
https://github.com/facebook/react/pull/34547

This PR updates getChangedHooksIndices to account for the fact that
`useSyncExternalStore`, `useTransition`, `useActionState`,
`useFormState` internally mounts more than one hook while DevTools
should treat it as a single user-facing hook.

Approach idea came from
[this](https://github.com/facebook/react/pull/34547#issuecomment-3504113776)
comment 😄

Before:


https://github.com/user-attachments/assets/6bd5ce80-8b52-4bb8-8bb1-5e91b1e65043


After:


https://github.com/user-attachments/assets/47f56898-ab34-46b6-be7a-a54024dcefee



## How did you test this change?

I used this component to reproduce this issue locally (I followed
instructions in `packages/react-devtools/CONTRIBUTING.md`).

<details><summary>Details</summary>

```ts

import * as React from 'react';

function useDeepNestedHook() {
  React.useState(0); // 1
  return React.useState(1); // 2
}

function useNestedHook() {
  const deepState = useDeepNestedHook();
  React.useState(2); // 3
  React.useState(3); // 4

  return deepState;
}

// Create a simple store for useSyncExternalStore
function createStore(initialValue) {
  let value = initialValue;
  const listeners = new Set();
  return {
    getSnapshot: () => value,
    subscribe: listener => {
      listeners.add(listener);
      return () => {
        listeners.delete(listener);
      };
    },
    update: newValue => {
      value = newValue;
      listeners.forEach(listener => listener());
    },
  };
}

const syncExternalStore = createStore(0);

export default function InspectableElements(): React.Node {
  const [nestedState, setNestedState] = useNestedHook();

  // 5
  const syncExternalValue = React.useSyncExternalStore(
    syncExternalStore.subscribe,
    syncExternalStore.getSnapshot,
  );

  // 6
  const [isPending, startTransition] = React.useTransition();

  // 7
  const [formState, formAction, formPending] = React.useActionState(
    async (prevState, formData) => {
      return {count: (prevState?.count || 0) + 1};
    },
    {count: 0},
  );

  const handleTransition = () => {
    startTransition(() => {
      setState(Math.random());
    });
  };

  // 8
  const [state, setState] = React.useState('test');

  return (
    <>
      <div
        style={{
          padding: '20px',
          display: 'flex',
          flexDirection: 'column',
          gap: '10px',
        }}>
        <div
          onClick={() => setNestedState(Math.random())}
          style={{backgroundColor: 'red', padding: '10px', cursor: 'pointer'}}>
          State: {nestedState}
        </div>

        <button onClick={handleTransition} style={{padding: '10px'}}>
          Trigger Transition {isPending ? '(pending...)' : ''}
        </button>

        <div style={{display: 'flex', gap: '10px', alignItems: 'center'}}>
          <button
            onClick={() => syncExternalStore.update(syncExternalValue + 1)}
            style={{padding: '10px'}}>
            Trigger useSyncExternalStore
          </button>
          <span>Value: {syncExternalValue}</span>
        </div>

        <form
          action={formAction}
          style={{display: 'flex', gap: '10px', alignItems: 'center'}}>
          <button
            type="submit"
            style={{padding: '10px'}}
            disabled={formPending}>
            Trigger useFormState {formPending ? '(pending...)' : ''}
          </button>
          <span>Count: {formState.count}</span>
        </form>

        <div
          onClick={() => setState(Math.random())}
          style={{backgroundColor: 'red', padding: '10px', cursor: 'pointer'}}>
          State: {state}
        </div>
      </div>
    </>
  );
}
```


</details>

---------

Co-authored-by: Ruslan Lesiutin <[email protected]>
2026-01-15 11:06:14 +00:00