Skip to content

Commit 073397a

Browse files
committed
Load environment names in the settings UI
By loading the latest value when clicking the button to open the settings.
1 parent 45d2c2e commit 073397a

File tree

8 files changed

+103
-13
lines changed

8 files changed

+103
-13
lines changed

packages/react-devtools-shared/src/backend/agent.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ export default class Agent extends EventEmitter<{
220220
this.updateConsolePatchSettings,
221221
);
222222
bridge.addListener('updateComponentFilters', this.updateComponentFilters);
223+
bridge.addListener('getEnvironmentNames', this.getEnvironmentNames);
223224

224225
// Temporarily support older standalone front-ends sending commands to newer embedded backends.
225226
// We do this because React Native embeds the React DevTools backend,
@@ -814,6 +815,24 @@ export default class Agent extends EventEmitter<{
814815
}
815816
};
816817

818+
getEnvironmentNames: () => void = () => {
819+
let accumulatedNames = null;
820+
for (const rendererID in this._rendererInterfaces) {
821+
const renderer = this._rendererInterfaces[+rendererID];
822+
const names = renderer.getEnvironmentNames();
823+
if (accumulatedNames === null) {
824+
accumulatedNames = names;
825+
} else {
826+
for (let i = 0; i < names.length; i++) {
827+
if (accumulatedNames.indexOf(names[i]) === -1) {
828+
accumulatedNames.push(names[i]);
829+
}
830+
}
831+
}
832+
}
833+
this._bridge.send('environmentNames', accumulatedNames || []);
834+
};
835+
817836
onTraceUpdates: (nodes: Set<HostInstance>) => void = nodes => {
818837
this.emit('traceUpdates', nodes);
819838
};

packages/react-devtools-shared/src/backend/fiber/renderer.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,10 @@ export function attach(
12261226
flushPendingEvents();
12271227
}
12281228

1229+
function getEnvironmentNames(): Array<string> {
1230+
return Array.from(knownEnvironmentNames);
1231+
}
1232+
12291233
function shouldFilterVirtual(
12301234
data: ReactComponentInfo,
12311235
secondaryEnv: null | string,
@@ -5801,5 +5805,6 @@ export function attach(
58015805
storeAsGlobal,
58025806
unpatchConsoleForStrictMode,
58035807
updateComponentFilters,
5808+
getEnvironmentNames,
58045809
};
58055810
}

packages/react-devtools-shared/src/backend/legacy/renderer.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,11 @@ export function attach(
10781078
// Not implemented.
10791079
}
10801080

1081+
function getEnvironmentNames(): Array<string> {
1082+
// No RSC support.
1083+
return [];
1084+
}
1085+
10811086
function setTraceUpdatesEnabled(enabled: boolean) {
10821087
// Not implemented.
10831088
}
@@ -1152,5 +1157,6 @@ export function attach(
11521157
storeAsGlobal,
11531158
unpatchConsoleForStrictMode,
11541159
updateComponentFilters,
1160+
getEnvironmentNames,
11551161
};
11561162
}

packages/react-devtools-shared/src/backend/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ export type RendererInterface = {
416416
) => void,
417417
unpatchConsoleForStrictMode: () => void,
418418
updateComponentFilters: (componentFilters: Array<ComponentFilter>) => void,
419+
getEnvironmentNames: () => Array<string>,
419420

420421
// Timeline profiler interface
421422

packages/react-devtools-shared/src/bridge.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export type BackendEvents = {
189189
operations: [Array<number>],
190190
ownersList: [OwnersList],
191191
overrideComponentFilters: [Array<ComponentFilter>],
192+
environmentNames: [Array<string>],
192193
profilingData: [ProfilingDataBackend],
193194
profilingStatus: [boolean],
194195
reloadAppForProfiling: [],
@@ -237,6 +238,7 @@ type FrontendEvents = {
237238
stopProfiling: [],
238239
storeAsGlobal: [StoreAsGlobalParams],
239240
updateComponentFilters: [Array<ComponentFilter>],
241+
getEnvironmentNames: [],
240242
updateConsolePatchSettings: [ConsolePatchSettings],
241243
viewAttributeSource: [ViewAttributeSourceParams],
242244
viewElementSource: [ElementAndRendererID],

packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
useMemo,
1616
useRef,
1717
useState,
18+
use,
1819
} from 'react';
1920
import {
2021
LOCAL_STORAGE_OPEN_IN_EDITOR_URL,
@@ -58,7 +59,11 @@ import type {
5859

5960
const vscodeFilepath = 'vscode://file/{path}:{line}';
6061

61-
export default function ComponentsSettings(_: {}): React.Node {
62+
export default function ComponentsSettings({
63+
environmentNames,
64+
}: {
65+
environmentNames: Promise<Array<string>>,
66+
}): React.Node {
6267
const store = useContext(StoreContext);
6368
const {parseHookNames, setParseHookNames} = useContext(SettingsContext);
6469

@@ -103,6 +108,23 @@ export default function ComponentsSettings(_: {}): React.Node {
103108
Array<ComponentFilter>,
104109
>(() => [...store.componentFilters]);
105110

111+
const usedEnvironmentNames = use(environmentNames);
112+
113+
const resolvedEnvironmentNames = useMemo(() => {
114+
const set = new Set(usedEnvironmentNames);
115+
// Client is special and is always available as a default.
116+
set.add('Client');
117+
// If there are other filters already specified but are not currently
118+
// on the page, we still allow them as options.
119+
for (let i = 0; i < componentFilters.length; i++) {
120+
const filter = componentFilters[i];
121+
if (filter.type === ComponentFilterEnvironmentName) {
122+
set.add(filter.value);
123+
}
124+
}
125+
return Array.from(set);
126+
}, [usedEnvironmentNames, componentFilters]);
127+
106128
const addFilter = useCallback(() => {
107129
setComponentFilters(prevComponentFilters => {
108130
return [
@@ -479,8 +501,11 @@ export default function ComponentsSettings(_: {}): React.Node {
479501
currentTarget.value,
480502
)
481503
}>
482-
<option value={'Client'}>Client</option>
483-
<option value={'Server'}>Server</option>
504+
{resolvedEnvironmentNames.map(name => (
505+
<option key={name} value={name}>
506+
{name}
507+
</option>
508+
))}
484509
</select>
485510
)}
486511
</td>

packages/react-devtools-shared/src/devtools/views/Settings/SettingsModal.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export default function SettingsModal(_: {}): React.Node {
5858
}
5959

6060
function SettingsModalImpl(_: {}) {
61-
const {setIsModalShowing} = useContext(SettingsModalContext);
61+
const {setIsModalShowing, environmentNames} =
62+
useContext(SettingsModalContext);
6263
const dismissModal = useCallback(
6364
() => setIsModalShowing(false),
6465
[setIsModalShowing],
@@ -81,7 +82,7 @@ function SettingsModalImpl(_: {}) {
8182
let view = null;
8283
switch (selectedTabID) {
8384
case 'components':
84-
view = <ComponentsSettings />;
85+
view = <ComponentsSettings environmentNames={environmentNames} />;
8586
break;
8687
// $FlowFixMe[incompatible-type] is this missing in TabID?
8788
case 'debugging':

packages/react-devtools-shared/src/devtools/views/Settings/SettingsModalContext.js

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,67 @@
1010
import type {ReactContext} from 'shared/ReactTypes';
1111

1212
import * as React from 'react';
13-
import {createContext, useMemo, useState} from 'react';
13+
import {
14+
createContext,
15+
useContext,
16+
useCallback,
17+
useState,
18+
startTransition,
19+
} from 'react';
20+
21+
import {BridgeContext} from '../context';
22+
import type {FrontendBridge} from '../../../bridge';
1423

1524
export type DisplayDensity = 'comfortable' | 'compact';
1625
export type Theme = 'auto' | 'light' | 'dark';
1726

1827
type Context = {
1928
isModalShowing: boolean,
2029
setIsModalShowing: (value: boolean) => void,
21-
...
30+
environmentNames: null | Promise<Array<string>>,
2231
};
2332

2433
const SettingsModalContext: ReactContext<Context> = createContext<Context>(
2534
((null: any): Context),
2635
);
2736
SettingsModalContext.displayName = 'SettingsModalContext';
2837

38+
function fetchEnvironmentNames(bridge: FrontendBridge): Promise<Array<string>> {
39+
return new Promise(resolve => {
40+
function onEnvironmentNames(names: Array<string>) {
41+
bridge.removeListener('environmentNames', onEnvironmentNames);
42+
resolve(names);
43+
}
44+
bridge.addListener('environmentNames', onEnvironmentNames);
45+
bridge.send('getEnvironmentNames');
46+
});
47+
}
48+
2949
function SettingsModalContextController({
3050
children,
3151
}: {
3252
children: React$Node,
3353
}): React.Node {
34-
const [isModalShowing, setIsModalShowing] = useState<boolean>(false);
54+
const bridge = useContext(BridgeContext);
3555

36-
const value = useMemo(
37-
() => ({isModalShowing, setIsModalShowing}),
38-
[isModalShowing, setIsModalShowing],
39-
);
56+
const setIsModalShowing: boolean => void = useCallback((value: boolean) => {
57+
startTransition(() => {
58+
setContext({
59+
isModalShowing: value,
60+
setIsModalShowing,
61+
environmentNames: value ? fetchEnvironmentNames(bridge) : null,
62+
});
63+
});
64+
});
65+
66+
const [currentContext, setContext] = useState<Context>({
67+
isModalShowing: false,
68+
setIsModalShowing,
69+
environmentNames: null,
70+
});
4071

4172
return (
42-
<SettingsModalContext.Provider value={value}>
73+
<SettingsModalContext.Provider value={currentContext}>
4374
{children}
4475
</SettingsModalContext.Provider>
4576
);

0 commit comments

Comments
 (0)