Files
react/packages/react-devtools-shared/src/backend/index.js
Ruslan Lesiutin 377c5175f7 DevTools: fix backend activation (#26779)
## Summary
We have a case:
1. Open components tab
2. Close Chrome / Firefox devtools window completely
3. Reopen browser devtools panel
4. Open components tab

Currently, in version 4.27.6, we cannot load the components tree.

This PR contains two changes:
- non-functional refactoring in
`react-devtools-shared/src/devtools/store.js`: removed some redundant
type castings.
- fixed backend manager logic (introduced in
https://github.com/facebook/react/pull/26615) to activate already
registered backends. Looks like frontend of devtools also depends on
`renderer-attached` event, without it component tree won't load.

## How did you test this change?
This fixes the case mentioned prior. Currently in 4.27.6 version it is
not working, we need to refresh the page to make it work.

I've tested this in several environments: chrome, firefox, standalone
with RN application.
2023-05-04 17:34:57 +01:00

137 lines
3.8 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 Agent from './agent';
import {attach} from './renderer';
import {attach as attachLegacy} from './legacy/renderer';
import {hasAssignedBackend} from './utils';
import type {DevToolsHook, ReactRenderer, RendererInterface} from './types';
// this is the backend that is compatible with all older React versions
function isMatchingRender(version: string): boolean {
return !hasAssignedBackend(version);
}
export type InitBackend = typeof initBackend;
export function initBackend(
hook: DevToolsHook,
agent: Agent,
global: Object,
): () => void {
if (hook == null) {
// DevTools didn't get injected into this page (maybe b'c of the contentType).
return () => {};
}
const subs = [
hook.sub(
'renderer-attached',
({
id,
renderer,
rendererInterface,
}: {
id: number,
renderer: ReactRenderer,
rendererInterface: RendererInterface,
...
}) => {
agent.setRendererInterface(id, rendererInterface);
// Now that the Store and the renderer interface are connected,
// it's time to flush the pending operation codes to the frontend.
rendererInterface.flushInitialOperations();
},
),
hook.sub('unsupported-renderer-version', (id: number) => {
agent.onUnsupportedRenderer(id);
}),
hook.sub('fastRefreshScheduled', agent.onFastRefreshScheduled),
hook.sub('operations', agent.onHookOperations),
hook.sub('traceUpdates', agent.onTraceUpdates),
// TODO Add additional subscriptions required for profiling mode
];
const attachRenderer = (id: number, renderer: ReactRenderer) => {
// 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.findFiberByHostInstance === 'function') {
// react-reconciler v16+
rendererInterface = attach(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
}
if (rendererInterface != null) {
hook.rendererInterfaces.set(id, rendererInterface);
}
}
// Notify the DevTools frontend about new renderers.
// This includes any that were attached early (via __REACT_DEVTOOLS_ATTACH__).
if (rendererInterface != null) {
hook.emit('renderer-attached', {
id,
renderer,
rendererInterface,
});
} else {
hook.emit('unsupported-renderer-version', id);
}
};
// Connect renderers that have already injected themselves.
hook.renderers.forEach((renderer, id) => {
attachRenderer(id, renderer);
});
// Connect any new renderers that injected themselves.
subs.push(
hook.sub(
'renderer',
({id, renderer}: {id: number, renderer: ReactRenderer, ...}) => {
attachRenderer(id, renderer);
},
),
);
hook.emit('react-devtools', agent);
hook.reactDevtoolsAgent = agent;
const onAgentShutdown = () => {
subs.forEach(fn => fn());
hook.rendererInterfaces.forEach(rendererInterface => {
rendererInterface.cleanup();
});
hook.reactDevtoolsAgent = null;
};
agent.addListener('shutdown', onAgentShutdown);
subs.push(() => {
agent.removeListener('shutdown', onAgentShutdown);
});
return () => {
subs.forEach(fn => fn());
};
}