Conditionally enable react-scan
Question
Hi there 👋, the README suggests the following to use react-scan programmatically:
import { scan } from 'react-scan'; // import this BEFORE react
import React from 'react';
if (typeof window !== 'undefined') {
scan({
enabled: true,
log: true, // logs render info to console (default: false)
});
}
However, if we want to only load react-scan in local builds, and allow for easy enabling and disabling, is that possible? If so, how?
Ideal API
My ideal API would be something like this:
import React from 'react';
function enableScanning() {
import('react-scan').then(({ scan }) => {
scan({
enabled: true,
log: true,
});
});
}
const AdminPanel = () => {
return (
...
<button onClick={enableScanning}>Enable React Scan</button>
<button onClick={disableScanning}>Disable React Scan</button>
...
)
}
But given the comment about react-scan needing to be imported before react, it seems like that won't be possible. Is that accurate?
This shouldn't be a problem to add (but currently not possible). We can probably add this in the next release
@jordan-cutler You can do something like this in your Layout.
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html>
{process.env.NODE_ENV === "development" && (
<head>
<script
src="https://unpkg.com/react-scan/dist/auto.global.js"
async
/>
</head>
)}
<body>
...
</body>
</html>
);
}
Definitely second this! Would be super useful to be able to toggle it on and off on a developer build.
Im using vite/typescript (not sure) conditional imports. Here's a complete configuration.
- Add the
importskey into package.json - Add the
devtools.dev.ts(with react-scan) anddevtools.prod.ts(empty) files - Import the files before react import (in your app entrypoint)
- Switch by
--mode productionor--mode developmentlike this
vite --mode development
vite build --mode production
From 5816eba0a62c14d386bb590bad4b0426c2a8a4d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20D=C5=82uski?=
Date: Sun, 20 Aug 2023 03:38:58 +0200
Subject: [PATCH] feat: make react-devtools import conditional (only on dev
build)
---
extension/package.json | 6 ++++++
extension/src/content/index.tsx | 2 +-
extension/src/devtools.dev.ts | 1 +
extension/src/devtools.prod.ts | 0
extension/tsconfig.json | 3 ++-
5 files changed, 10 insertions(+), 2 deletions(-)
create mode 100644 extension/src/devtools.dev.ts
create mode 100644 extension/src/devtools.prod.ts
diff --git a/extension/package.json b/extension/package.json
index 51ece72d..73a673da 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -98,5 +98,11 @@
"vite": "^4.4.6",
"vite-plugin-eslint": "^1.8.1",
"vitest": "^0.33.0"
+ },
+ "imports": {
+ "#env-import/*.ts": {
+ "development": "./src/*.dev.ts",
+ "production": "./src/*.prod.ts"
+ }
}
}
diff --git a/extension/src/content/index.tsx b/extension/src/content/index.tsx
index 07e630eb..254243dd 100644
--- a/extension/src/content/index.tsx
+++ b/extension/src/content/index.tsx
@@ -1,4 +1,4 @@
-import 'react-devtools';
+import '#env-import/devtools.ts';
import React from 'react';
import { createRoot } from 'react-dom/client';
diff --git a/extension/src/devtools.dev.ts b/extension/src/devtools.dev.ts
new file mode 100644
index 00000000..b33e2a07
--- /dev/null
+++ b/extension/src/devtools.dev.ts
@@ -0,0 +1 @@
+import 'react-devtools';
\ No newline at end of file
diff --git a/extension/src/devtools.prod.ts b/extension/src/devtools.prod.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/extension/tsconfig.json b/extension/tsconfig.json
index 52defc17..569dd3f9 100644
--- a/extension/tsconfig.json
+++ b/extension/tsconfig.json
@@ -11,7 +11,7 @@
"ESNext"
],
"module": "ESNext",
- "moduleResolution": "node",
+ "moduleResolution": "bundler",
"noImplicitReturns": true,
"noUnusedLocals": true,
"strict": true,
@@ -25,6 +25,7 @@
"isolatedModules": true,
"noEmit": true,
"allowJs": true,
+ "allowImportingTsExtensions": true,
"typeRoots": [
"./types",
"./node_modules/@types"
--
6.45.2.windows.1
I'd like to add react scan locally in dev, but also be able to toggle it on/off.
For example, I have some local-only UI widget that has some common dev utils. I'd like to add a button there toggle react scan on/off. Is this possible?
I'd like to add react scan locally in dev, but also be able to toggle it on/off.
For example, I have some local-only UI widget that has some common dev utils. I'd like to add a button there toggle react scan on/off. Is this possible?
https://github.com/aidenybai/react-scan/issues/79
I hacked together a custom vite plugin for now, but would love to see a vite-plugin as a first-class citizen:
import { Plugin } from 'vite';
export default function reactScanPlugin(options = {}) {
const defaultScanConfig = {
enabled: true,
log: true,
};
const scanConfig = { ...defaultScanConfig, ...options };
return {
name: 'vite-plugin-react-scan',
enforce: 'post',
transform(code, id) {
if (id.endsWith('main.jsx') || id.endsWith('main.tsx')) {
const scanScript = `
import { scan } from 'react-scan';
if (typeof window !== 'undefined') {
scan(${JSON.stringify(scanConfig)});
}
`;
return {
code: `${scanScript}\n${code}`,
map: null,
};
}
},
};
}
I hacked together a custom vite plugin for now, but would love to see a vite-plugin as a first-class citizen:
import { Plugin } from 'vite'; export default function reactScanPlugin(options = {}) { const defaultScanConfig = { enabled: true, log: true, }; const scanConfig = { ...defaultScanConfig, ...options }; return { name: 'vite-plugin-react-scan', enforce: 'post', transform(code, id) { if (id.endsWith('main.jsx') || id.endsWith('main.tsx')) { const scanScript = ` import { scan } from 'react-scan'; if (typeof window !== 'undefined') { scan(${JSON.stringify(scanConfig)}); } `; return { code: `${scanScript}\n${code}`, map: null, }; } }, }; }
How is that different than conditional import apart it wont work that easily in chrome extensions or other targets where the main.tsx is gonna have a different name?
I hacked together a custom vite plugin for now, but would love to see a vite-plugin as a first-class citizen:
import { Plugin } from 'vite'; export default function reactScanPlugin(options = {}) { const defaultScanConfig = { enabled: true, log: true, }; const scanConfig = { ...defaultScanConfig, ...options }; return { name: 'vite-plugin-react-scan', enforce: 'post', transform(code, id) { if (id.endsWith('main.jsx') || id.endsWith('main.tsx')) { const scanScript = ` import { scan } from 'react-scan'; if (typeof window !== 'undefined') { scan(${JSON.stringify(scanConfig)}); } `; return { code: `${scanScript}\n${code}`, map: null, }; } }, }; }How is that different than conditional import apart it wont work that easily in chrome extensions or other targets where the main.tsx is gonna have a different name?
@Toumash you're free to do whatever works best for your setup. Vite plugins seem to be the standard for layering devtools into a JavaScript bundle with vite, and since that's how my current application is structured, I didn’t want to use any alternative mechanisms.
You’re right that this won't support entrypoints that are not named 'main' - there’s probably a better way to determine the entry point. Worst-case scenario, it could be explicitly provided as a parameter to the plugin.
For now, I just wanted to share my approach in case it inspires others to improve on it or build a more robust and extendable Vite plugin that can handle a variety of workflows.
Ok, thank you for clarification. I'm just being curious.
For now, I just wanted to share my approach in case it inspires others to improve on it or build a more robust and extendable Vite plugin that can handle a variety of workflows.
I'm actually seeing a vite plugin's code for the first time - thank you!
@Toumash @DAcodedBEAT you guys should trye this one: @pivanov/vite-plugin-react-scan :)
please let me know how it's work! then we can close this one, right?