mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Detect React Native v3 backend and show warning
This commit is contained in:
@@ -86,6 +86,7 @@ function reload() {
|
||||
bridge: ((bridge: any): Bridge),
|
||||
showTabBar: true,
|
||||
store: ((store: any): Store),
|
||||
warnIfLegacyBackendDetected: true,
|
||||
viewElementSourceFunction,
|
||||
viewElementSourceRequiresFileLocation: true,
|
||||
})
|
||||
|
||||
@@ -77,6 +77,7 @@ inject('dist/app.js', () => {
|
||||
browserTheme: 'light',
|
||||
showTabBar: true,
|
||||
store,
|
||||
warnIfLegacyBackendDetected: true,
|
||||
})
|
||||
);
|
||||
batch.then(() => {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -38,6 +39,7 @@ export type Props = {|
|
||||
defaultTab?: TabID,
|
||||
showTabBar?: boolean,
|
||||
store: Store,
|
||||
warnIfLegacyBackendDetected?: boolean,
|
||||
viewElementSourceFunction?: ?ViewElementSource,
|
||||
viewElementSourceRequiresFileLocation?: boolean,
|
||||
|
||||
@@ -80,6 +82,7 @@ export default function DevTools({
|
||||
settingsPortalContainer,
|
||||
showTabBar = false,
|
||||
store,
|
||||
warnIfLegacyBackendDetected = false,
|
||||
viewElementSourceFunction,
|
||||
viewElementSourceRequiresFileLocation = false,
|
||||
}: Props) {
|
||||
@@ -143,6 +146,7 @@ export default function DevTools({
|
||||
</TreeContextController>
|
||||
</ViewElementSourceContext.Provider>
|
||||
</SettingsContextController>
|
||||
{warnIfLegacyBackendDetected && <WarnIfLegacyBackendDetected />}
|
||||
</ModalDialogContextController>
|
||||
</StoreContext.Provider>
|
||||
</BridgeContext.Provider>
|
||||
|
||||
@@ -18,6 +18,7 @@ type DIALOG_ACTION_HIDE = {|
|
||||
|};
|
||||
type DIALOG_ACTION_SHOW = {|
|
||||
type: 'SHOW',
|
||||
canBeDismissed?: boolean,
|
||||
content: React$Node,
|
||||
title?: React$Node | null,
|
||||
|};
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -67,6 +71,7 @@ type Props = {|
|
||||
|
||||
function ModalDialogContextController({ children }: Props) {
|
||||
const [state, dispatch] = useReducer<State, Action>(dialogReducer, {
|
||||
canBeDismissed: true,
|
||||
content: null,
|
||||
isVisible: false,
|
||||
title: null,
|
||||
@@ -74,6 +79,7 @@ function ModalDialogContextController({ children }: Props) {
|
||||
|
||||
const value = useMemo<ModalDialogContextType>(
|
||||
() => ({
|
||||
canBeDismissed: state.canBeDismissed,
|
||||
content: state.content,
|
||||
isVisible: state.isVisible,
|
||||
title: state.title,
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
6
src/devtools/views/WarnIfLegacyBackendDetected.css
Normal file
6
src/devtools/views/WarnIfLegacyBackendDetected.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.Command {
|
||||
background-color: var(--color-dimmest);
|
||||
padding: 0.25rem 0.5rem;
|
||||
display: block;
|
||||
border-radius: 0.125rem;
|
||||
}
|
||||
60
src/devtools/views/WarnIfLegacyBackendDetected.js
Normal file
60
src/devtools/views/WarnIfLegacyBackendDetected.js
Normal file
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user