@samitouri / QOS-React-2 / commits / 249a2e043d

Detect React Native v3 backend and show warning

Brian Vaughn committed Jul 18, 2019 at 16:24 UTC 249a2e043dbd261bf54a95d0893dd973f4489d57
7 files changed +99 -9
packages/react-devtools-core/src/standalone.js
+1
@@ -86,6 +86,7 @@ function reload() {
86 bridge: ((bridge: any): Bridge),
87 showTabBar: true,
88 store: ((store: any): Store),
89 + warnIfLegacyBackendDetected: true,
90 viewElementSourceFunction,
91 viewElementSourceRequiresFileLocation: true,
92 })
shells/dev/src/devtools.js
+1
@@ -77,6 +77,7 @@ inject('dist/app.js', () => {
77 browserTheme: 'light',
78 showTabBar: true,
79 store,
80 + warnIfLegacyBackendDetected: true,
81 })
82 );
83 batch.then(() => {
src/bridge.js
+6
@@ -128,6 +128,12 @@ export default class Bridge extends EventEmitter<{|
128 }) || null;
129 }
130
131 + // Listening directly to the wall isn't advised.
132 + // It can be used to listen for legacy (v3) messages (since they use a different format).
133 + get wall(): Wall {
134 + return this._wall;
135 + }
136 +
137 send(event: string, payload: any, transferable?: Array<any>) {
138 if (this._isShutdown) {
139 console.warn(
src/devtools/views/DevTools.js
+4
@@ -18,6 +18,7 @@ import ViewElementSourceContext from './Components/ViewElementSourceContext';
18 import { ProfilerContextController } from './Profiler/ProfilerContext';
19 import { ModalDialogContextController } from './ModalDialog';
20 import ReactLogo from './ReactLogo';
21 +import WarnIfLegacyBackendDetected from './WarnIfLegacyBackendDetected';
22
23 import styles from './DevTools.css';
24
@@ -38,6 +39,7 @@ export type Props = {|
39 defaultTab?: TabID,
40 showTabBar?: boolean,
41 store: Store,
42 + warnIfLegacyBackendDetected?: boolean,
43 viewElementSourceFunction?: ?ViewElementSource,
44 viewElementSourceRequiresFileLocation?: boolean,
45
@@ -80,6 +82,7 @@ export default function DevTools({
82 settingsPortalContainer,
83 showTabBar = false,
84 store,
85 + warnIfLegacyBackendDetected = false,
86 viewElementSourceFunction,
87 viewElementSourceRequiresFileLocation = false,
88 }: Props) {
@@ -143,6 +146,7 @@ export default function DevTools({
146 </TreeContextController>
147 </ViewElementSourceContext.Provider>
148 </SettingsContextController>
149 + {warnIfLegacyBackendDetected && <WarnIfLegacyBackendDetected />}
150 </ModalDialogContextController>
151 </StoreContext.Provider>
152 </BridgeContext.Provider>
src/devtools/views/ModalDialog.js
+21 -9
@@ -18,6 +18,7 @@ type DIALOG_ACTION_HIDE = {|
18 |};
19 type DIALOG_ACTION_SHOW = {|
20 type: 'SHOW',
21 + canBeDismissed?: boolean,
22 content: React$Node,
23 title?: React$Node | null,
24 |};
@@ -27,6 +28,7 @@ type Action = DIALOG_ACTION_HIDE | DIALOG_ACTION_SHOW;
28 type Dispatch = (action: Action) => void;
29
30 type State = {|
31 + canBeDismissed: boolean,
32 content: React$Node | null,
33 isVisible: boolean,
34 title: React$Node | null,
@@ -46,12 +48,14 @@ function dialogReducer(state, action) {
48 switch (action.type) {
49 case 'HIDE':
50 return {
51 + canBeDismissed: true,
52 content: null,
53 isVisible: false,
54 title: null,
55 };
56 case 'SHOW':
57 return {
58 + canBeDismissed: action.canBeDismissed !== false,
59 content: action.content,
60 isVisible: true,
61 title: action.title || null,
@@ -67,6 +71,7 @@ type Props = {|
71
72 function ModalDialogContextController({ children }: Props) {
73 const [state, dispatch] = useReducer<State, Action>(dialogReducer, {
74 + canBeDismissed: true,
75 content: null,
76 isVisible: false,
77 title: null,
@@ -74,6 +79,7 @@ function ModalDialogContextController({ children }: Props) {
79
80 const value = useMemo<ModalDialogContextType>(
81 () => ({
82 + canBeDismissed: state.canBeDismissed,
83 content: state.content,
84 isVisible: state.isVisible,
85 title: state.title,
@@ -95,10 +101,14 @@ function ModalDialog(_: {||}) {
101 }
102
103 function ModalDialogImpl(_: {||}) {
98 - const { content, dispatch, title } = useContext(ModalDialogContext);
99 - const dismissModal = useCallback(() => dispatch({ type: 'HIDE' }), [
100 - dispatch,
101 - ]);
104 + const { canBeDismissed, content, dispatch, title } = useContext(
105 + ModalDialogContext
106 + );
107 + const dismissModal = useCallback(() => {
108 + if (canBeDismissed) {
109 + dispatch({ type: 'HIDE' });
110 + }
111 + }, [canBeDismissed, dispatch]);
112 const modalRef = useRef<HTMLDivElement | null>(null);
113
114 useModalDismissSignal(modalRef, dismissModal);
@@ -108,11 +118,13 @@ function ModalDialogImpl(_: {||}) {
118 <div className={styles.Dialog} ref={modalRef}>
119 {title !== null && <div className={styles.Title}>{title}</div>}
120 {content}
111 - <div className={styles.Buttons}>
112 - <Button autoFocus onClick={dismissModal}>
113 - Okay
114 - </Button>
115 - </div>
121 + {canBeDismissed && (
122 + <div className={styles.Buttons}>
123 + <Button autoFocus onClick={dismissModal}>
124 + Okay
125 + </Button>
126 + </div>
127 + )}
128 </div>
129 </div>
130 );
src/devtools/views/WarnIfLegacyBackendDetected.css new
+6
@@ -0,0 +1,6 @@
1 +.Command {
2 + background-color: var(--color-dimmest);
3 + padding: 0.25rem 0.5rem;
4 + display: block;
5 + border-radius: 0.125rem;
6 +}
src/devtools/views/WarnIfLegacyBackendDetected.js new
+60
@@ -0,0 +1,60 @@
1 +// @flow
2 +
3 +import React, { Fragment, useContext, useEffect } from 'react';
4 +import { BridgeContext } from './context';
5 +import { ModalDialogContext } from './ModalDialog';
6 +
7 +import styles from './WarnIfLegacyBackendDetected.css';
8 +
9 +export default function WarnIfLegacyBackendDetected(_: {||}) {
10 + const bridge = useContext(BridgeContext);
11 + const { dispatch } = useContext(ModalDialogContext);
12 +
13 + // Detect pairing with legacy v3 backend.
14 + // We do this by listening to a message that it broadcasts but the v4 backend doesn't.
15 + // In this case the frontend should show upgrade instructions.
16 + useEffect(() => {
17 + // Wall.listen returns a cleanup function
18 + let unlisten = bridge.wall.listen(event => {
19 + switch (event.type) {
20 + case 'call':
21 + case 'event':
22 + case 'many-events':
23 + // Any of these types indicate the v3 backend.
24 + dispatch({
25 + canBeDismissed: false,
26 + type: 'SHOW',
27 + title:
28 + 'React DevTools v4 is incompatible with this version of React',
29 + content: <InvalidBackendDetected />,
30 + });
31 +
32 + if (typeof unlisten === 'function') {
33 + unlisten();
34 + unlisten = null;
35 + }
36 + break;
37 + default:
38 + break;
39 + }
40 + });
41 +
42 + return () => {
43 + if (typeof unlisten === 'function') {
44 + unlisten();
45 + unlisten = null;
46 + }
47 + };
48 + }, [bridge, dispatch]);
49 +
50 + return null;
51 +}
52 +
53 +function InvalidBackendDetected(_: {||}) {
54 + return (
55 + <Fragment>
56 + <p>Either upgrade React or install React DevTools v3:</p>
57 + <code className={styles.Command}>npm install -d react-devtools@^3</code>
58 + </Fragment>
59 + );
60 +}