Skip to content

Commit 5f692ac

Browse files
fix(reactotron-react-native): only set DevMenu when in __DEV__ (#1527 by @frankcalise and @joshuayoes)
## Please verify the following: - [x] `yarn build-and-test:local` passes - [ ] I have added tests for any new features, if relevant - [ ] `README.md` (or relevant documentation) has been updated with your changes ## Describe your PR - Closes #1513 - Before we improperly included the DevMenu module in both debug and release builds, this checks the `__DEV__` flag - Now we access the module using `TurboModuleRegistry.get` instead of `TurboModuleRegistry.getEnforcing` to avoid throwing a top level JS error when it does not exist --------- Co-authored-by: Joshua Yoes <[email protected]>
1 parent ed7c16e commit 5f692ac

File tree

4 files changed

+75
-17
lines changed

4 files changed

+75
-17
lines changed

.eslintrc.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module.exports = {
3232
"react-native/Libraries/LogBox/Data/parseLogBoxLog.js",
3333
"react-native/Libraries/LogBox/LogBox.js",
3434
"react-native/Libraries/Core/NativeExceptionsManager.js",
35+
"react-native/Libraries/NativeModules/specs/NativeDevMenu.js",
3536
],
3637
},
3738
rules: {
@@ -57,4 +58,4 @@ module.exports = {
5758
"scripts",
5859
"**/CHANGELOG.md",
5960
],
60-
};
61+
}

apps/example-app/app/devtools/ReactotronConfig.ts

+7-12
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* free desktop app for inspecting and debugging your React Native app.
44
* @see https://github.com/infinitered/reactotron
55
*/
6-
import { Platform } from "react-native"
6+
import { Platform, TurboModuleRegistry } from "react-native"
77

88
import AsyncStorage from "@react-native-async-storage/async-storage"
99
import { ArgType } from "reactotron-core-client"
@@ -16,16 +16,6 @@ import { goBack, resetRoot, navigate } from "app/navigators/navigationUtilities"
1616

1717
import { Reactotron } from "./ReactotronClient"
1818

19-
let DevMenu = null
20-
/**
21-
* This Platform.OS iOS restriction can be lifted in React Native 0.77
22-
* The `DevMenu` module was missing in Android for the New Architecture
23-
* See this PR for more details: https://github.com/facebook/react-native/pull/46723
24-
*/
25-
if (Platform.OS === "ios") {
26-
DevMenu = require("react-native/Libraries/NativeModules/specs/NativeDevMenu")
27-
}
28-
2919
const reactotron = Reactotron.configure({
3020
name: require("../../package.json").name,
3121
onConnect: () => {
@@ -64,14 +54,19 @@ if (Platform.OS !== "web") {
6454
* or else your custom commands won't be registered correctly.
6555
*/
6656

57+
/**
58+
* This Platform.OS iOS restriction can be lifted in React Native 0.77
59+
* The `DevMenu` module was missing in Android for the New Architecture
60+
* See this PR for more details: https://github.com/facebook/react-native/pull/46723
61+
*/
6762
if (Platform.OS === "ios") {
6863
reactotron.onCustomCommand({
6964
title: "Show Dev Menu",
7065
description: "Opens the React Native dev menu",
7166
command: "showDevMenu",
7267
handler: () => {
7368
Reactotron.log("Showing React Native dev menu")
74-
DevMenu.show()
69+
TurboModuleRegistry.get<{ show: () => void; getConstants: () => {} }>("DevMenu")?.show()
7570
},
7671
})
7772
}

lib/reactotron-react-native/src/plugins/devTools.ts

+49-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,52 @@
1-
import { Platform } from "react-native"
1+
import { Platform, TurboModuleRegistry } from "react-native"
22
import type { ReactotronCore, Plugin } from "reactotron-core-client"
3+
import type { Spec } from "react-native/Libraries/NativeModules/specs/NativeDevMenu"
34

4-
let DevMenu = { show: () => {}, reload: () => {} }
5-
if (Platform.OS === "ios") {
6-
DevMenu = require("react-native/Libraries/NativeModules/specs/NativeDevMenu")
5+
/**
6+
* Lazily get the DevMenu module.
7+
*
8+
* This avoids trying a potentially risky call to React Native internals unless our dear developer actually wants to use it.
9+
*/
10+
const getDevMenu = (): Spec => {
11+
const notAvailable = (method: string) => {
12+
console.warn(`DevMenu.${method}() not available in this environment`)
13+
}
14+
15+
const stubDevMenu: Spec = {
16+
reload() {
17+
notAvailable("reload")
18+
},
19+
show() {
20+
notAvailable("show")
21+
},
22+
getConstants() {
23+
return {}
24+
},
25+
debugRemotely() {
26+
notAvailable("debugRemotely")
27+
},
28+
setHotLoadingEnabled() {
29+
notAvailable("setHotLoadingEnabled")
30+
},
31+
setProfilingEnabled() {
32+
notAvailable("setProfilingEnabled")
33+
},
34+
}
35+
36+
if (Platform.OS === "ios" && __DEV__) {
37+
try {
38+
// use TurboModuleRegistry.get instead of TurboModuleRegistry.getEnforcing, like at
39+
// https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/specs/modules/NativeDevMenu.js#L23
40+
const DevMenu = TurboModuleRegistry.get<Spec>("DevMenu")
41+
// the DevMenu module is not available in all environments, like Expo Go, so we need to check if it exists
42+
if (DevMenu) return DevMenu
43+
return stubDevMenu
44+
} catch {
45+
return stubDevMenu
46+
}
47+
}
48+
49+
return stubDevMenu
750
}
851

952
const devTools = () => () => {
@@ -12,10 +55,12 @@ const devTools = () => () => {
1255
if (command.type !== "devtools.open" && command.type !== "devtools.reload") return
1356

1457
if (command.type === "devtools.open") {
58+
const DevMenu = getDevMenu()
1559
DevMenu.show()
1660
}
1761

1862
if (command.type === "devtools.reload") {
63+
const DevMenu = getDevMenu()
1964
DevMenu.reload()
2065
}
2166
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
declare module "react-native/Libraries/NativeModules/specs/NativeDevMenu" {
2+
import type { TurboModule } from "react-native"
3+
/**
4+
* @see https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/NativeModules/specs/NativeDevMenu.js
5+
* @see https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/specs/modules/NativeDevMenu.js#L15-L21
6+
*/
7+
export interface Spec extends TurboModule {
8+
show: () => void
9+
reload: () => void
10+
debugRemotely: (enableDebug: boolean) => void
11+
setProfilingEnabled: (enabled: boolean) => void
12+
setHotLoadingEnabled: (enabled: boolean) => void
13+
}
14+
15+
const DevMenu: Spec
16+
export default DevMenu
17+
}

0 commit comments

Comments
 (0)