Files
react/packages/react-devtools-shared/src/attachRenderer.js
T
Ruslan Lesiutin bb6b86ed59 refactor[react-devtools]: initialize renderer interface early (#30946)
The current state is that `rendererInterface`, which contains all the
backend logic, like generating component stack or attaching errors to
fibers, or traversing the Fiber tree, ..., is only mounted after the
Frontend is created.

For browser extension, this means that we don't patch console or track
errors and warnings before Chrome DevTools is opened.

With these changes, `rendererInterface` is created right after
`renderer` is injected from React via global hook object (e. g.
`__REACT_DEVTOOLS_GLOBAL_HOOK__.inject(...)`.

Because of the current implementation, in case of multiple Reacts on the
page, all of them will patch the console independently. This will be
fixed in one of the next PRs, where I am moving console patching to the
global Hook.

This change of course makes `hook.js` script bigger, but I think it is a
reasonable trade-off for better DevX. We later can add more heuristics
to optimize the performance (if necessary) of `rendererInterface` for
cases when Frontend was connected late and Backend is attempting to
flush out too many recorded operations.

This essentially reverts https://github.com/facebook/react/pull/26563.
2024-09-12 13:59:29 +01:00

62 lines
2.0 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {
ReactRenderer,
RendererInterface,
DevToolsHook,
RendererID,
} from 'react-devtools-shared/src/backend/types';
import {attach as attachFlight} from 'react-devtools-shared/src/backend/flight/renderer';
import {attach as attachFiber} from 'react-devtools-shared/src/backend/fiber/renderer';
import {attach as attachLegacy} from 'react-devtools-shared/src/backend/legacy/renderer';
import {hasAssignedBackend} from 'react-devtools-shared/src/backend/utils';
// this is the backend that is compatible with all older React versions
function isMatchingRender(version: string): boolean {
return !hasAssignedBackend(version);
}
export default function attachRenderer(
hook: DevToolsHook,
id: RendererID,
renderer: ReactRenderer,
global: Object,
): RendererInterface | void {
// only attach if the renderer is compatible with the current version of the backend
if (!isMatchingRender(renderer.reconcilerVersion || renderer.version)) {
return;
}
let rendererInterface = hook.rendererInterfaces.get(id);
// Inject any not-yet-injected renderers (if we didn't reload-and-profile)
if (rendererInterface == null) {
if (typeof renderer.getCurrentComponentInfo === 'function') {
// react-flight/client
rendererInterface = attachFlight(hook, id, renderer, global);
} else if (
// v16-19
typeof renderer.findFiberByHostInstance === 'function' ||
// v16.8+
renderer.currentDispatcherRef != null
) {
// react-reconciler v16+
rendererInterface = attachFiber(hook, id, renderer, global);
} else if (renderer.ComponentTree) {
// react-dom v15
rendererInterface = attachLegacy(hook, id, renderer, global);
} else {
// Older react-dom or other unsupported renderer version
}
}
return rendererInterface;
}