反应惰性递归导入

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> }))
);