反应惰性递归导入
React lazy recursive import
我有需要从动态文件夹加载组件的要求。例如,我在 components
中有以下文件夹
components
-default
-component-one
-component-two
-component-three
-custom
-component-three
假设如果 componentFolder state
设置为 custom
文件夹,那么它应该从 custom folder
加载。如果在 custom folder
中找不到任何组件,那么它应该从 default
文件夹。
所以我的问题是,我们可以递归导入吗?
function App() {
const [componentFolder, setComponentFolder] = React.useState("default")
const Home = React.lazy(() => import("./components/" +componentFolder+ "/Home"));
return (
<div className="App">
<Suspense fallback="laoding">
<Home></Home>
</Suspense>
</div>
);
}
下面link的要求和我问的一样
因为 lazy
return 是一个承诺,你可以使用它的 catch
块来 return 另一个 lazy
(承诺)当原始模块不是找到了。
例子:
import { lazy, Suspense, useState } from "react";
const rotate = {
custom: "default",
default: "custom",
};
function App() {
const [folder, setFolder] = useState("custom");
const [name, setName] = useState("component1");
// Here: catch and return another lazy (promise)
const Component = lazy(() =>
import("./components/" + folder + "/" + name).catch(
(err) => import("./components/" + rotate[folder] + "/" + name)
)
);
return (
<div>
<Suspense fallback="laoding">
<Component />
</Suspense>
<button onClick={() => setFolder(rotate[folder])}>toggle folder</button>
<br />
<button onClick={() => setName("component1")}>load component 1</button>
<button onClick={() => setName("component2")}>load component 2</button>
<button onClick={() => setName("component3")}>load component 3</button>
</div>
);
}
这里是demo.
请注意 App
组件内的 Component
、defined/created 将在 App
的每次重新渲染时 重新创建 。 .
如果您使用的是 Webpack,那么您可以使用 require.context 动态加载模块:
import React, { Suspense } from "react";
const load = async (path, file) => {
const defaultPath = "default";
const files = require.context("./components", true, /\.js$/);
try {
return files(`./${path}/${file}.js`);
} catch (err) {
return files(`./${defaultPath}/${file}.js`);
}
};
export default function App() {
const [componentFolder, setComponentFolder] = React.useState("default");
const Home = React.lazy(() => load(componentFolder, "Home"));
return (
<div className="App">
<Suspense fallback="loading">
<Home />
</Suspense>
</div>
);
}
我正在尝试一些东西,最后得到一个你应该达到的简单解决方案:
https://codesandbox.io/s/awesome-violet-fr7np?file=/src/App.js
根据这里的其他答案和评论,我想到了这个:
https://codesandbox.io/s/so-react-lazy-recursive-import-2dqlp?file=/src/App.js
import React, { lazy, Suspense } from "react";
// test the code by removing the _ in front of file names
/*
components/
comp 3? a root file will not trigger -> go to default
/default
comp 3! nice ^^ (but if it not exists will throw an error)
/custom
comp 2?
/client
comp 1?
/omgStop
heIsAlreadyDead (but works)
/otherClient ...
*/
const recursiveImport = async (
componentName,
targetTree,
defaultTree = "./components/default"
) => {
console.count("paths tested");
if (!targetTree) {
return import(defaultTree + "/" + componentName);
}
return import("./components/" + targetTree + "/" + componentName).catch(
() => {
const newTreeArr = targetTree.split("/");
newTreeArr.pop();
const newTree = newTreeArr.join("/");
return recursiveImport(componentName, newTree, defaultTree);
}
);
};
export default function App() {
const targetTree = "custom/client1";
const Component = lazy(() => recursiveImport("Test", targetTree));
return (
<div>
<Suspense fallback="loading">{<Component />}</Suspense>
</div>
);
}
文件夹结构:
这能解决您的所有需求吗?
简单又objective
const Recipe = React.lazy(() =>
import(`docs/app/Recipes/${props.componentName}`)
.catch(() => ({ default: () => <div>Not found</div> }))
);
我有需要从动态文件夹加载组件的要求。例如,我在 components
中有以下文件夹components
-default
-component-one
-component-two
-component-three
-custom
-component-three
假设如果 componentFolder state
设置为 custom
文件夹,那么它应该从 custom folder
加载。如果在 custom folder
中找不到任何组件,那么它应该从 default
文件夹。
所以我的问题是,我们可以递归导入吗?
function App() {
const [componentFolder, setComponentFolder] = React.useState("default")
const Home = React.lazy(() => import("./components/" +componentFolder+ "/Home"));
return (
<div className="App">
<Suspense fallback="laoding">
<Home></Home>
</Suspense>
</div>
);
}
下面link的要求和我问的一样
因为 lazy
return 是一个承诺,你可以使用它的 catch
块来 return 另一个 lazy
(承诺)当原始模块不是找到了。
例子:
import { lazy, Suspense, useState } from "react";
const rotate = {
custom: "default",
default: "custom",
};
function App() {
const [folder, setFolder] = useState("custom");
const [name, setName] = useState("component1");
// Here: catch and return another lazy (promise)
const Component = lazy(() =>
import("./components/" + folder + "/" + name).catch(
(err) => import("./components/" + rotate[folder] + "/" + name)
)
);
return (
<div>
<Suspense fallback="laoding">
<Component />
</Suspense>
<button onClick={() => setFolder(rotate[folder])}>toggle folder</button>
<br />
<button onClick={() => setName("component1")}>load component 1</button>
<button onClick={() => setName("component2")}>load component 2</button>
<button onClick={() => setName("component3")}>load component 3</button>
</div>
);
}
这里是demo.
请注意 App
组件内的 Component
、defined/created 将在 App
的每次重新渲染时 重新创建 。
如果您使用的是 Webpack,那么您可以使用 require.context 动态加载模块:
import React, { Suspense } from "react";
const load = async (path, file) => {
const defaultPath = "default";
const files = require.context("./components", true, /\.js$/);
try {
return files(`./${path}/${file}.js`);
} catch (err) {
return files(`./${defaultPath}/${file}.js`);
}
};
export default function App() {
const [componentFolder, setComponentFolder] = React.useState("default");
const Home = React.lazy(() => load(componentFolder, "Home"));
return (
<div className="App">
<Suspense fallback="loading">
<Home />
</Suspense>
</div>
);
}
我正在尝试一些东西,最后得到一个你应该达到的简单解决方案:
https://codesandbox.io/s/awesome-violet-fr7np?file=/src/App.js
根据这里的其他答案和评论,我想到了这个:
https://codesandbox.io/s/so-react-lazy-recursive-import-2dqlp?file=/src/App.js
import React, { lazy, Suspense } from "react";
// test the code by removing the _ in front of file names
/*
components/
comp 3? a root file will not trigger -> go to default
/default
comp 3! nice ^^ (but if it not exists will throw an error)
/custom
comp 2?
/client
comp 1?
/omgStop
heIsAlreadyDead (but works)
/otherClient ...
*/
const recursiveImport = async (
componentName,
targetTree,
defaultTree = "./components/default"
) => {
console.count("paths tested");
if (!targetTree) {
return import(defaultTree + "/" + componentName);
}
return import("./components/" + targetTree + "/" + componentName).catch(
() => {
const newTreeArr = targetTree.split("/");
newTreeArr.pop();
const newTree = newTreeArr.join("/");
return recursiveImport(componentName, newTree, defaultTree);
}
);
};
export default function App() {
const targetTree = "custom/client1";
const Component = lazy(() => recursiveImport("Test", targetTree));
return (
<div>
<Suspense fallback="loading">{<Component />}</Suspense>
</div>
);
}
文件夹结构:
这能解决您的所有需求吗?
简单又objective
const Recipe = React.lazy(() =>
import(`docs/app/Recipes/${props.componentName}`)
.catch(() => ({ default: () => <div>Not found</div> }))
);