在 React Router Routes 中动态注入数据

Alf*_*les 6 javascript modularization reactjs react-router

我一直在尝试模块化我的 React.js 应用程序(它将作为带有 Electron 的桌面应用程序交付),如果我将来创建一个新模块,我只需添加一个新文件夹并修改一个几个文件,它应该可以很好地集成。我最初受到这篇文章的启发:https : //www.nylas.com/blog/react-plugins/

在那之后,我开始尽可能多地进行研究,最终创建了一个 JSON 文件,该文件将存在于服务器中,其中包含为该特定客户端注册的插件清单。像这样的东西:

{
    "plugins": [
        {
            "name": "Test Plugin",
            "version": "0.0.1",
            "path": "testplugin",
            "file": "test",
            "component":"TestPlugin"
        },
        {
            "name": "Another Plugin",
            "version": "0.0.1",
            "path": "anothertest",
            "file": "othertest",
            "component":"TestPluginDeux"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

之后,我创建了几个与path值匹配的文件夹,其中包含一个与清单中的名称匹配的组件(例如testplugin/test.jsx,将TestPlugin组件导出为默认值)。我还制作了一个pluginStore文件,用于读取清单并将插件安装在this.state.

然后,在谷歌和这里做了大量研究并找到了这个答案:React - Dynamically Import Components

使用该函数,我能够遍历清单,找到目录中的文件夹,并this.state通过运行mountPlugins()我在主页中pluginStorecomponentDidMount()方法中创建的函数来安装插件。

到现在为止还挺好。我使用的阵营,路由器和我能够在国家动态地安装插件,并能够通过只要求他们这样加载它们在我的主页路线:<TestPlugin />

我现在遇到的问题是,我想动态创建路由,通过使用componentrender方法从状态加载这些组件,但我没有运气。我总是会得到相同的结果......显然我传递的是一个对象而不是一个字符串。

这是我在这次尝试中的最后一次迭代:

{this.state.modules.registered.map((item) =>
<Route exact path={`/${item.path}`} render={function() {
  return <item.component />
  }}></Route>
)}
Run Code Online (Sandbox Code Playgroud)

之后,我创建了一个路由,它调用了一个由 a 调用的PluginShell组件,该组件Navlink发送插件的名称以动态注入和加载它。

<Route exact path='/ex/:component' component={PluginShell}></Route>
Run Code Online (Sandbox Code Playgroud)

但我最终遇到了同样的问题。我正在传递一个object并且该createElement函数期望一个string.

我在 StackOverflow 上到处搜索,发现了许多类似的问题和答案。我尝试应用所有可能的解决方案,但没有运气。

编辑:我已经整理了一个 GitHub 存储库,其中包含最少的文件集来重现该问题。

这是链接:https : //codesandbox.io/embed/aged-moon-nrrjc

Mat*_*tta 5

好吧,波基。这里有很多活动部件可以大大简化。

  1. 我建议转向对开发人员更友好、更固执己见的状态存储(如Redux)。我个人从未使用过 Flux,所以我只能推荐我有经验的。因此,您可以避免使用普通类进行状态管理。
  2. 您应该在初始应用程序加载期间只导入一次模块,然后您可以调度一个动作将它们存储到(Redux)状态,然后根据需要与组件共享状态(仅当状态要与许多组件共享时才需要)分布在您的 DOM 树中,否则根本不需要)。
  3. 模块导入是异步的,因此无法立即加载。在将模块映射到 a 之前,您必须设置一个条件以等待加载模块Route(在您的情况下,您试图将模块的注册字符串名称映射到路由,而不是导入的模块函数)。
  4. 理想情况下,模块导入应包含在状态内的已注册模块中。换句话说,当你导入模块时,它应该只是用一个 Component 函数覆盖模块 Component 字符串。这样,所有相关信息都放在一个对象中。
  5. 无需将模板文字与字符串连接混合和匹配。使用其中之一。
  6. 使用setState 回调previousState在覆盖之前传播任何内容。看起来更简单,更干净。
  7. 将您的 import 语句包裹在一个try/catch块中,否则,如果该模块不存在,它可能会破坏您的应用程序。

工作示例(我只是在这个简单示例中使用 React 状态,我也没有触及任何其他文件,这些文件也可以简化):

编辑 wispy-thunder-jtc6c


应用程序.js

import React from "react";
import Navigation from "./components/MainNavigation";
import Routes from "./routes";
import { plugins } from "./modules/manifest.json";
import "./assets/css/App.css";

class App extends React.Component {
  state = {
    importedModules: []
  };

  componentDidMount = () => {
    this.importPlugins();
  };

  importPlugins = () => {
    if (plugins) {
      try {
        const importedModules = [];
        const importPromises = plugins.map(plugin =>
          import(`./modules/${plugin.path}/${plugin.file}`).then(module => {
            importedModules.push({ ...plugin, Component: module.default });
          })
        );

        Promise.all(importPromises).then(() =>
          this.setState(prevState => ({
            ...prevState,
            importedModules
          }))
        );
      } catch (err) {
        console.error(err.toString());
      }
    }
  };

  render = () => (
    <div className="App">
      <Navigation />
      <Routes {...this.state} />
    </div>
  );
}

export default App;
Run Code Online (Sandbox Code Playgroud)

路线/ index.js

import React from "react";
import React from "react";
import isEmpty from "lodash/isEmpty";
import { Switch, Route } from "react-router-dom";
import ProjectForm from "../modules/core/forms/new-project-form";
import NewPostForm from "../modules/core/forms/new-post-form";
import ProjectLoop from "../modules/core/loops/project-loop";
import Home from "../home";

const Routes = ({ importedModules }) => (
  <Switch>
    <Route exact path="/" component={Home} />
    <Route exact path="/projectlist/:filter" component={ProjectLoop} />
    <Route exact path="/newproject/:type/:id" component={ProjectForm} />
    <Route exact path="/newpost/:type" component={NewPostForm} />
    {!isEmpty(importedModules) &&
      importedModules.map(({ path, Component }) => (
        <Route key={path} exact path={`/${path}`} component={Component} />
      ))}
  </Switch>
);

export default Routes;
Run Code Online (Sandbox Code Playgroud)