无法在React应用中执行条件导入文件



我在React Typescript应用程序中根据process.env.WORLD值导入JSON文件时遇到了问题。这是在定义React上下文的.tsx文件中完成的,这里没有使用React组件。

当我们忽略process.env变量时,JSON文件可以正常加载。

import data from '../main.json';
  • TS文件加载JSON文件:src/contexts/world.tsx
  • JSON文件:src/main.json

在我的第一次尝试中,我尝试

let file;
if (process.env.WORLD == 'main') {
file = '../main.json';
} else {
file = '../test.json';
}
import data from file;

但是得到错误

Import in body of module; reorder to top  import/first

尝试使用动态import

const data = await import(
process.env.WORLD == 'main'
? '../main.json'
: '../test.json'
);

但是我的设置不允许顶级await

Module parse failed: Cannot use keyword 'await' outside an async function 

尝试使用require代替import

let file;
if (process.env.WORLD === 'main') {
file = '../main.json';
} else {
file = '../test.json';
}
const data = require(file);

但是它几乎可以工作,除了找不到文件

contexts sync:2 Uncaught Error: Cannot find module '../main.json'

奇怪的是,与import使用相同的文件路径没有问题,如本问题的第一个示例所示。


Typescript目标是ES2018,并且使用React 18。

为什么使用require时找不到文件,如何解决这个问题?

在评论中,您添加了暗示外部JSON数据被用作ContextProvidervalue的一部分的信息。

通过在渲染前动态获取(或导入)JSON数据,你可以在初始渲染时通过props将其传递给上下文提供程序,然后在ref中记忆它。下面是堆栈溢出代码片段中的一个工作示例,它使用对象url代替本地文件说明符。

请注意,在如何优化外部数据加载方面有很多考虑因素,你的问题没有提供关于你的场景的太多细节,但最适合你的应用程序的方法将取决于这些细节。在任何情况下,你在问题中所描述的将更好地通过重写磁盘上的单个JSON文件的内容(基于环境变量)之前构建或运行你的应用程序每次处理。这需要在之外处理。通过一个外部进程(比如npm脚本),可以让你完全避免这种情况,同时还可以通过静态导入优化构建结果。

打印稿操场

<div id="root"></div><script src="https://unpkg.com/react@18.2.0/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.18.8/babel.min.js"></script><script>Babel.registerPreset('tsx', {presets: [[Babel.availablePresets['typescript'], {allExtensions: true, isTSX: true}]]});</script>
<script type="text/babel" data-type="module" data-presets="tsx,react">
// import ReactDOM from 'react-dom/client';
// import {
//   default as React,
//   createContext,
//   StrictMode,
//   useContext,
//   useRef,
//   type ReactNode,
//   type ReactElement,
// } from 'react';
// This Stack Overflow snippet demo uses UMD modules
// instead of the commented import statments above
const {
createContext,
StrictMode,
useContext,
useRef,
} = React;
type JsonData = {
items: string[];
listTitle: string;
};
// An initial value is not required as long as this
// is not used outside the context provider:
const jsonContext = createContext(undefined as unknown as JsonData);
function JsonProvider ({children, data}: {
children: ReactNode;
data: JsonData;
}): ReactElement {
const ref = useRef(data);
return (
<jsonContext.Provider value={ref.current}>
{children}
</jsonContext.Provider>
);
}
function App (): ReactElement {
const {items, listTitle} = useContext(jsonContext);
return (
<div>
<h1>{listTitle}</h1>
<ul>
{items.map((item, index) => (<li key={index}>{item}</li>))}
</ul>
</div>
);
}
type JsonPrimitive = boolean | null | number | string;
type JsonSerializable = JsonPrimitive | JsonSerializable[] | { [key: string]: JsonSerializable };
function createJsonObjectUrl (serializable: JsonSerializable): string {
const json = JSON.stringify(serializable);
const blob = new Blob([json], {type: 'application/json'});
return URL.createObjectURL(blob);
}
async function inititalRender () {
// The Stack Overflow code snippet sandbox doesn't have Node's `process`,
// so we'll simulate it here:
const process = {
env: {
// Simulate that it could be either "main" or "test"
REACT_APP_JSON_FILE: Math.random() < 0.5 ? 'main' : 'test',
} as Partial<Record<string, string>>,
};
// In your app, this would simply be "../main.json" or "../test.json",
// but the Stack Overflow code snippet sandbox doesn't have access to
// your local files, so we'll simulate the specifier with an object URL
// (which can also be fetched/imported):
const specifier = process.env.REACT_APP_JSON_FILE === 'main'
? createJsonObjectUrl({
items: ['a', 'b', 'c'],
listTitle: 'main',
})
: createJsonObjectUrl({
items: ['one', 'two', 'three'],
listTitle: 'test',
});
// Use fetch to get the data (we're in an async function, so that's OK!)
const data = await (await fetch(specifier)).json();
// Or (in the future) dynamic import with an assertion:
// const {default: data} = await import(specifier, {assert: {type: 'json'}});
const reactRoot = ReactDOM.createRoot(document.getElementById('root')!);
reactRoot.render(
<StrictMode>
<JsonProvider {...{data}}>
<App />
</JsonProvider>
</StrictMode>
);
}
inititalRender();
</script>

可以同时导入两个文件,然后选择合适的文件吗?

import main from '../main.json';
import test from  '../test.json';

let file;
if (process.env.WORLD == 'main') {
file = main;
} else {
file = test
}

或使用react.lazy:

const Questions = React.lazy(() => { 
if (process.env.WORLD == 'main') {
import('../main.json'))
} else {
import('../test.json'))
}
}

最新更新