Skip to content

Commit

Permalink
Detect React Native v3 backend and show warning
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn committed Jul 18, 2019
1 parent 9fb753b commit 249a2e0
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/react-devtools-core/src/standalone.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ function reload() {
bridge: ((bridge: any): Bridge),
showTabBar: true,
store: ((store: any): Store),
warnIfLegacyBackendDetected: true,
viewElementSourceFunction,
viewElementSourceRequiresFileLocation: true,
})
Expand Down
1 change: 1 addition & 0 deletions shells/dev/src/devtools.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ inject('dist/app.js', () => {
browserTheme: 'light',
showTabBar: true,
store,
warnIfLegacyBackendDetected: true,
})
);
batch.then(() => {
Expand Down
6 changes: 6 additions & 0 deletions src/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ export default class Bridge extends EventEmitter<{|
}) || null;
}

// Listening directly to the wall isn't advised.
// It can be used to listen for legacy (v3) messages (since they use a different format).
get wall(): Wall {
return this._wall;
}

send(event: string, payload: any, transferable?: Array<any>) {
if (this._isShutdown) {
console.warn(
Expand Down
4 changes: 4 additions & 0 deletions src/devtools/views/DevTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ViewElementSourceContext from './Components/ViewElementSourceContext';
import { ProfilerContextController } from './Profiler/ProfilerContext';
import { ModalDialogContextController } from './ModalDialog';
import ReactLogo from './ReactLogo';
import WarnIfLegacyBackendDetected from './WarnIfLegacyBackendDetected';

import styles from './DevTools.css';

Expand All @@ -38,6 +39,7 @@ export type Props = {|
defaultTab?: TabID,
showTabBar?: boolean,
store: Store,
warnIfLegacyBackendDetected?: boolean,
viewElementSourceFunction?: ?ViewElementSource,
viewElementSourceRequiresFileLocation?: boolean,

Expand Down Expand Up @@ -80,6 +82,7 @@ export default function DevTools({
settingsPortalContainer,
showTabBar = false,
store,
warnIfLegacyBackendDetected = false,
viewElementSourceFunction,
viewElementSourceRequiresFileLocation = false,
}: Props) {
Expand Down Expand Up @@ -143,6 +146,7 @@ export default function DevTools({
</TreeContextController>
</ViewElementSourceContext.Provider>
</SettingsContextController>
{warnIfLegacyBackendDetected && <WarnIfLegacyBackendDetected />}
</ModalDialogContextController>
</StoreContext.Provider>
</BridgeContext.Provider>
Expand Down
30 changes: 21 additions & 9 deletions src/devtools/views/ModalDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type DIALOG_ACTION_HIDE = {|
|};
type DIALOG_ACTION_SHOW = {|
type: 'SHOW',
canBeDismissed?: boolean,
content: React$Node,
title?: React$Node | null,
|};
Expand All @@ -27,6 +28,7 @@ type Action = DIALOG_ACTION_HIDE | DIALOG_ACTION_SHOW;
type Dispatch = (action: Action) => void;

type State = {|
canBeDismissed: boolean,
content: React$Node | null,
isVisible: boolean,
title: React$Node | null,
Expand All @@ -46,12 +48,14 @@ function dialogReducer(state, action) {
switch (action.type) {
case 'HIDE':
return {
canBeDismissed: true,
content: null,
isVisible: false,
title: null,
};
case 'SHOW':
return {
canBeDismissed: action.canBeDismissed !== false,
content: action.content,
isVisible: true,
title: action.title || null,
Expand All @@ -67,13 +71,15 @@ type Props = {|

function ModalDialogContextController({ children }: Props) {
const [state, dispatch] = useReducer<State, Action>(dialogReducer, {
canBeDismissed: true,
content: null,
isVisible: false,
title: null,
});

const value = useMemo<ModalDialogContextType>(
() => ({
canBeDismissed: state.canBeDismissed,
content: state.content,
isVisible: state.isVisible,
title: state.title,
Expand All @@ -95,10 +101,14 @@ function ModalDialog(_: {||}) {
}

function ModalDialogImpl(_: {||}) {
const { content, dispatch, title } = useContext(ModalDialogContext);
const dismissModal = useCallback(() => dispatch({ type: 'HIDE' }), [
dispatch,
]);
const { canBeDismissed, content, dispatch, title } = useContext(
ModalDialogContext
);
const dismissModal = useCallback(() => {
if (canBeDismissed) {
dispatch({ type: 'HIDE' });
}
}, [canBeDismissed, dispatch]);
const modalRef = useRef<HTMLDivElement | null>(null);

useModalDismissSignal(modalRef, dismissModal);
Expand All @@ -108,11 +118,13 @@ function ModalDialogImpl(_: {||}) {
<div className={styles.Dialog} ref={modalRef}>
{title !== null && <div className={styles.Title}>{title}</div>}
{content}
<div className={styles.Buttons}>
<Button autoFocus onClick={dismissModal}>
Okay
</Button>
</div>
{canBeDismissed && (
<div className={styles.Buttons}>
<Button autoFocus onClick={dismissModal}>
Okay
</Button>
</div>
)}
</div>
</div>
);
Expand Down
6 changes: 6 additions & 0 deletions src/devtools/views/WarnIfLegacyBackendDetected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.Command {
background-color: var(--color-dimmest);
padding: 0.25rem 0.5rem;
display: block;
border-radius: 0.125rem;
}
60 changes: 60 additions & 0 deletions src/devtools/views/WarnIfLegacyBackendDetected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// @flow

import React, { Fragment, useContext, useEffect } from 'react';
import { BridgeContext } from './context';
import { ModalDialogContext } from './ModalDialog';

import styles from './WarnIfLegacyBackendDetected.css';

export default function WarnIfLegacyBackendDetected(_: {||}) {
const bridge = useContext(BridgeContext);
const { dispatch } = useContext(ModalDialogContext);

// Detect pairing with legacy v3 backend.
// We do this by listening to a message that it broadcasts but the v4 backend doesn't.
// In this case the frontend should show upgrade instructions.
useEffect(() => {
// Wall.listen returns a cleanup function
let unlisten = bridge.wall.listen(event => {
switch (event.type) {
case 'call':
case 'event':
case 'many-events':
// Any of these types indicate the v3 backend.
dispatch({
canBeDismissed: false,
type: 'SHOW',
title:
'React DevTools v4 is incompatible with this version of React',
content: <InvalidBackendDetected />,
});

if (typeof unlisten === 'function') {
unlisten();
unlisten = null;
}
break;
default:
break;
}
});

return () => {
if (typeof unlisten === 'function') {
unlisten();
unlisten = null;
}
};
}, [bridge, dispatch]);

return null;
}

function InvalidBackendDetected(_: {||}) {
return (
<Fragment>
<p>Either upgrade React or install React DevTools v3:</p>
<code className={styles.Command}>npm install -d react-devtools@^3</code>
</Fragment>
);
}

0 comments on commit 249a2e0

Please sign in to comment.