Reputation: 3192
I have requirement where I need to load component from dynamic folders. For example I have following folders inside components
components
-default
-component-one
-component-two
-component-three
-custom
-component-three
Suppose if componentFolder state
set to custom
folder then it should load from custom folder
.if any component not found in custom folder
then it should be load from default
folder.
So my question is ,can we possible to import recursively ?
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>
);
}
the below link has same requirement as i asked How to check if a pariticular fileExists in reactjs
Upvotes: 3
Views: 2682
Reputation: 830
Simple and objective
const Recipe = React.lazy(() =>
import(`docs/app/Recipes/${props.componentName}`)
.catch(() => ({ default: () => <div>Not found</div> }))
);
Upvotes: 0
Reputation: 1006
Based in the others answers and comments here I came up with this:
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>
);
}
Folder structure:
This solves all of your requirements?
Upvotes: 1
Reputation: 1051
I was trying something, endup with a simple solution that you should reach before:
https://codesandbox.io/s/awesome-violet-fr7np?file=/src/App.js
Upvotes: -2
Reputation: 13078
If you are using Webpack then you can use require.context to load modules dynamically:
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>
);
}
Upvotes: 4
Reputation: 19853
Since lazy
returns a promise, you can use its catch
block to return another lazy
(promise) when the original module was not found.
An example:
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>
);
}
Here is a demo.
Note that Component
, defined/created inside App
component, will be recreated at every rerender of App
. It will cause Component to reset its state when App rerenders.
Upvotes: 2