DevTools shows unsupported renderer version dialog (#16897)
* DevTools shows unsupported renderer version dialog * Optimistic CHANGELOG udpate
Brian Vaughn committed
Sep 26, 2019 at 08:41 UTC
b6606ecba80b591ea66db1e2ee1fed72befb4c32
12 files changed
+161
-10
packages/react-devtools-extensions/src/main.js
+1
-1
@@ -161,7 +161,7 @@ function createPanelIfReactLoaded() {
161
overrideTab,
162
profilerPortalContainer,
163
showTabBar: false,
164
- showWelcomeToTheNewDevToolsDialog: true,
164
+ warnIfUnsupportedVersionDetected: true,
165
store,
166
viewElementSourceFunction,
167
}),
packages/react-devtools-shared/src/backend/agent.js
+4
@@ -474,6 +474,10 @@ export default class Agent extends EventEmitter<{|
474
}
475
};
476
477
+ onUnsupportedRenderer(rendererID: number) {
478
+ this._bridge.send('unsupportedRendererVersion', rendererID);
479
+ }
480
+
481
_throttledPersistSelection = throttle((rendererID: number, id: number) => {
482
// This is throttled, so both renderer and selected ID
483
// might not be available by the time we read them.
packages/react-devtools-shared/src/backend/index.js
+22
-8
@@ -39,6 +39,10 @@ export function initBackend(
39
},
40
),
41
42
+ hook.sub('unsupported-renderer-version', (id: number) => {
43
+ agent.onUnsupportedRenderer(id);
44
+ }),
45
+
46
hook.sub('operations', agent.onHookOperations),
47
48
// TODO Add additional subscriptions required for profiling mode
@@ -48,23 +52,33 @@ export function initBackend(
52
let rendererInterface = hook.rendererInterfaces.get(id);
53
54
// Inject any not-yet-injected renderers (if we didn't reload-and-profile)
51
- if (!rendererInterface) {
55
+ if (rendererInterface == null) {
56
if (typeof renderer.findFiberByHostInstance === 'function') {
57
+ // react-reconciler v16+
58
rendererInterface = attach(hook, id, renderer, global);
54
- } else {
59
+ } else if (renderer.ComponentTree) {
60
+ // react-dom v15
61
rendererInterface = attachLegacy(hook, id, renderer, global);
62
+ } else {
63
+ // Older react-dom or other unsupported renderer version
64
}
65
58
- hook.rendererInterfaces.set(id, rendererInterface);
66
+ if (rendererInterface != null) {
67
+ hook.rendererInterfaces.set(id, rendererInterface);
68
+ }
69
}
70
71
// Notify the DevTools frontend about new renderers.
72
// This includes any that were attached early (via __REACT_DEVTOOLS_ATTACH__).
63
- hook.emit('renderer-attached', {
64
- id,
65
- renderer,
66
- rendererInterface,
67
- });
73
+ if (rendererInterface != null) {
74
+ hook.emit('renderer-attached', {
75
+ id,
76
+ renderer,
77
+ rendererInterface,
78
+ });
79
+ } else {
80
+ hook.emit('unsupported-renderer-version', id);
81
+ }
82
};
83
84
// Connect renderers that have already injected themselves.
packages/react-devtools-shared/src/backend/types.js
+4
-1
@@ -81,7 +81,10 @@ export type ReactRenderer = {
81
// Enables DevTools to append owners-only component stack to error messages.
82
getCurrentFiber?: () => Fiber | null,
83
84
- // <= 15
84
+ // Uniquely identifies React DOM v15.
85
+ ComponentTree?: any,
86
+
87
+ // Present for React DOM v12 (possibly earlier) through v15.
88
Mount?: any,
89
};
90
packages/react-devtools-shared/src/bridge.js
+1
@@ -83,6 +83,7 @@ type BackendEvents = {|
83
stopInspectingNative: [boolean],
84
syncSelectionFromNativeElementsPanel: [],
85
syncSelectionToNativeElementsPanel: [],
86
+ unsupportedRendererVersion: [RendererID],
87
88
// React Native style editor plug-in.
89
isNativeStyleEditorSupported: [
packages/react-devtools-shared/src/constants.js
+3
@@ -35,6 +35,9 @@ export const PROFILER_EXPORT_VERSION = 4;
35
export const CHANGE_LOG_URL =
36
'https://github.com/facebook/react/blob/master/packages/react-devtools/CHANGELOG.md';
37
38
+export const UNSUPPORTED_VERSION_URL =
39
+ 'https://reactjs.org/blog/2019/08/15/new-react-devtools.html#how-do-i-get-the-old-version-back';
40
+
41
// HACK
42
//
43
// Extracting during build time avoids a temporarily invalid state for the inline target.
packages/react-devtools-shared/src/devtools/store.js
+17
@@ -73,6 +73,7 @@ export default class Store extends EventEmitter<{|
73
supportsNativeStyleEditor: [],
74
supportsProfiling: [],
75
supportsReloadAndProfile: [],
76
+ unsupportedRendererVersionDetected: [],
77
|}> {
78
_bridge: FrontendBridge;
79
@@ -125,6 +126,8 @@ export default class Store extends EventEmitter<{|
126
_supportsProfiling: boolean = false;
127
_supportsReloadAndProfile: boolean = false;
128
129
+ _unsupportedRendererVersionDetected: boolean = false;
130
+
131
// Total number of visible elements (within all roots).
132
// Used for windowing purposes.
133
_weightAcrossRoots: number = 0;
@@ -179,6 +182,10 @@ export default class Store extends EventEmitter<{|
182
'isNativeStyleEditorSupported',
183
this.onBridgeNativeStyleEditorSupported,
184
);
185
+ bridge.addListener(
186
+ 'unsupportedRendererVersion',
187
+ this.onBridgeUnsupportedRendererVersion,
188
+ );
189
190
this._profilerStore = new ProfilerStore(bridge, this, isProfiling);
191
}
@@ -337,6 +344,10 @@ export default class Store extends EventEmitter<{|
344
return this._supportsReloadAndProfile && this._isBackendStorageAPISupported;
345
}
346
347
+ get unsupportedRendererVersionDetected(): boolean {
348
+ return this._unsupportedRendererVersionDetected;
349
+ }
350
+
351
containsElement(id: number): boolean {
352
return this._idToElement.get(id) != null;
353
}
@@ -1009,4 +1020,10 @@ export default class Store extends EventEmitter<{|
1020
1021
this.emit('supportsReloadAndProfile');
1022
};
1023
+
1024
+ onBridgeUnsupportedRendererVersion = () => {
1025
+ this._unsupportedRendererVersionDetected = true;
1026
+
1027
+ this.emit('unsupportedRendererVersionDetected');
1028
+ };
1029
}
packages/react-devtools-shared/src/devtools/views/DevTools.js
+4
@@ -24,6 +24,7 @@ import ViewElementSourceContext from './Components/ViewElementSourceContext';
24
import {ProfilerContextController} from './Profiler/ProfilerContext';
25
import {ModalDialogContextController} from './ModalDialog';
26
import ReactLogo from './ReactLogo';
27
+import UnsupportedVersionDialog from './UnsupportedVersionDialog';
28
import WarnIfLegacyBackendDetected from './WarnIfLegacyBackendDetected';
29
30
import styles from './DevTools.css';
@@ -51,6 +52,7 @@ export type Props = {|
52
showTabBar?: boolean,
53
store: Store,
54
warnIfLegacyBackendDetected?: boolean,
55
+ warnIfUnsupportedVersionDetected?: boolean,
56
viewElementSourceFunction?: ?ViewElementSource,
57
58
// This property is used only by the web extension target.
@@ -92,6 +94,7 @@ export default function DevTools({
94
showTabBar = false,
95
store,
96
warnIfLegacyBackendDetected = false,
97
+ warnIfUnsupportedVersionDetected = false,
98
viewElementSourceFunction,
99
}: Props) {
100
const [tab, setTab] = useState(defaultTab);
@@ -164,6 +167,7 @@ export default function DevTools({
167
</ViewElementSourceContext.Provider>
168
</SettingsContextController>
169
{warnIfLegacyBackendDetected && <WarnIfLegacyBackendDetected />}
170
+ {warnIfUnsupportedVersionDetected && <UnsupportedVersionDialog />}
171
</ModalDialogContextController>
172
</StoreContext.Provider>
173
</BridgeContext.Provider>
packages/react-devtools-shared/src/devtools/views/UnsupportedVersionDialog.css
new
+20
@@ -0,0 +1,20 @@
1
+.Row {
2
+ display: flex;
3
+ flex-direction: row;
4
+ align-items: center;
5
+}
6
+
7
+.Column {
8
+ display: flex;
9
+ flex-direction: column;
10
+ align-items: center;
11
+}
12
+
13
+.Title {
14
+ font-size: var(--font-size-sans-large);
15
+ margin-bottom: 0.5rem;
16
+}
17
+
18
+.ReleaseNotesLink {
19
+ color: var(--color-button-active);
20
+}
\ No newline at end of file
packages/react-devtools-shared/src/devtools/views/UnsupportedVersionDialog.js
new
+83
@@ -0,0 +1,83 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+import React, {Fragment, useContext, useEffect, useState} from 'react';
11
+import {unstable_batchedUpdates as batchedUpdates} from 'react-dom';
12
+import {ModalDialogContext} from './ModalDialog';
13
+import {StoreContext} from './context';
14
+import {UNSUPPORTED_VERSION_URL} from 'react-devtools-shared/src/constants';
15
+
16
+import styles from './UnsupportedVersionDialog.css';
17
+
18
+type DAILOG_STATE = 'dialog-not-shown' | 'show-dialog' | 'dialog-shown';
19
+
20
+export default function UnsupportedVersionDialog(_: {||}) {
21
+ const {dispatch} = useContext(ModalDialogContext);
22
+ const store = useContext(StoreContext);
23
+ const [state, setState] = useState<DAILOG_STATE>('dialog-not-shown');
24
+
25
+ useEffect(
26
+ () => {
27
+ if (state === 'dialog-not-shown') {
28
+ const showDialog = () => {
29
+ batchedUpdates(() => {
30
+ setState('show-dialog');
31
+ dispatch({
32
+ canBeDismissed: true,
33
+ type: 'SHOW',
34
+ content: <DialogContent />,
35
+ });
36
+ });
37
+ };
38
+
39
+ if (store.unsupportedRendererVersionDetected) {
40
+ showDialog();
41
+ } else {
42
+ store.addListener('unsupportedRendererVersionDetected', showDialog);
43
+ return () => {
44
+ store.removeListener(
45
+ 'unsupportedRendererVersionDetected',
46
+ showDialog,
47
+ );
48
+ };
49
+ }
50
+ }
51
+ },
52
+ [state, store],
53
+ );
54
+
55
+ return null;
56
+}
57
+
58
+function DialogContent(_: {||}) {
59
+ return (
60
+ <Fragment>
61
+ <div className={styles.Row}>
62
+ <div>
63
+ <div className={styles.Title}>Unsupported React version detected</div>
64
+ <p>
65
+ This version of React DevTools supports React DOM v15+ and React
66
+ Native v61+.
67
+ </p>
68
+ <p>
69
+ In order to use DevTools with an older version of React, you'll need
70
+ to{' '}
71
+ <a
72
+ className={styles.ReleaseNotesLink}
73
+ target="_blank"
74
+ rel="noopener noreferrer"
75
+ href={UNSUPPORTED_VERSION_URL}>
76
+ install an older version of the extension
77
+ </a>.
78
+ </p>
79
+ </div>
80
+ </div>
81
+ </Fragment>
82
+ );
83
+}
packages/react-devtools-shell/src/devtools.js
+1
@@ -57,6 +57,7 @@ inject('dist/app.js', () => {
57
browserTheme: 'light',
58
showTabBar: true,
59
warnIfLegacyBackendDetected: true,
60
+ warnIfUnsupportedVersionDetected: true,
61
}),
62
);
63
},
packages/react-devtools/CHANGELOG.md
+1
@@ -12,6 +12,7 @@
12
* Fixed bug where Components panel was always empty for certain users. ([linshunghuang](https://github.com/linshunghuang) in [#16900](https://github.com/facebook/react/pull/16900))
13
* Fixed regression in DevTools editable hooks interface that caused primitive values to be shown as `undefined`. ([bvaughn](https://github.com/bvaughn) in [#16867](https://github.com/facebook/react/pull/16867))
14
* Fixed bug where DevTools showed stale values in props/state/hooks editing interface. ([bvaughn](https://github.com/bvaughn) in [#16878](https://github.com/facebook/react/pull/16878))
15
+* Show unsupported version dialog with downgrade instructions. ([bvaughn](https://github.com/bvaughn) in [#16897](https://github.com/facebook/react/pull/16897))
16
</details>
17
18
## 4.1.0 (September 19, 2019)