3

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

scott
  • 3,112
  • 19
  • 52
  • 90

5 Answers5

4

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>
  );
}
lissettdm
  • 12,267
  • 1
  • 18
  • 39
  • @lissettdm.thanks for the answer .i am not using reactjs using create react app.i am not sure its webpack or not.since i am beginner to reactjs . – scott Apr 18 '21 at 03:48
  • What module bundler are you using? can you check that? – lissettdm Apr 18 '21 at 03:54
  • i sorry my previous comment has wrong typo.I am using reactjs using create react app https://create-react-app.dev/.Also i am okay with different solution other than lazy loading if my requirement satisfies – scott Apr 18 '21 at 05:04
  • CRA use Webpack as module bundle. You can use require.context to load modules dynamically and you can combine it with Lazy load – lissettdm Apr 18 '21 at 14:23
2

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.

Ajeet Shah
  • 18,551
  • 8
  • 57
  • 87
  • thanks but i am looking for recursive since it might be multiple parent child hierarchy so .For example default->custom->client-specific then it will first check in client -specific then it will check in custom then its go to default .is there any solution's will try this anyway – scott Apr 13 '21 at 14:12
  • Hm. I thought you wrote "recursive" by mistake as I saw no recursive path in your example. – Ajeet Shah Apr 13 '21 at 14:14
  • @ajeeh.yes i have tried many methods but couldn't succeeded so .even other solution other than lazy loading also fine.thanks for your effort – scott Apr 13 '21 at 14:18
1

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:

enter image description here

This solves all of your requirements?

Jorge Kunrath
  • 817
  • 7
  • 17
  • Note : bounty question expires in 2 hours so giving bounty to one of the below answers for their effort .i will try this soon and let you know . – scott Apr 19 '21 at 11:58
0

Simple and objective

const Recipe = React.lazy(() =>
  import(`docs/app/Recipes/${props.componentName}`)
  .catch(() => ({ default: () => <div>Not found</div> }))
);
Aloiso Gomes
  • 640
  • 6
  • 12
-2

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

Vinicius Katata
  • 932
  • 3
  • 7