【问题标题】:Dynamic React Router and component not possible?动态反应路由器和组件不可能?
【发布时间】:2021-06-27 19:06:39
【问题描述】:

我正在尝试使用 React 构建动态路由器。这个想法是从后端接收到的数据(对象)创建路由:

菜单对象:

items: [
    {
      name: "dashboard",
      icon: "dashboard",
      placeholder: "Dashboard",
      path: "/",
      page: "Dashboard",
      exact: true,
    },
    {
      name: "suppliers",
      icon: "suppliers",
      placeholder: "Suppliers",
      path: "/suppliers",
      page: "Suppliers",
      exact: true,
    }
]

路线挂钩:

export const RouteHook = () => {
  // Dispatch to fetch the menu object from the backend
  const dispatch = useDispatch();
  dispatch(setMenu());

  // Select the menu and items from Redux
  const { items } = useSelector(getMainMenu);

  // useState to set the routes inside
  const [menuItems, setMenuItems] = useState([]);

  
  const { pathname } = useLocation();

  useEffect(() => {
    // Loop trough the menu Items
    for (const item of items) {
      const { page, path, name, exact } = item;

      // Import dynamically the route components
      import(`../pages/${name}/${page}`).then((result) => {
        // Set the routes inside the useState
        setMenuItems(
          <Route exact={exact} path={path} component={result[page]} />
        );
      });
    }
     // Check if pathname has changed to update the useEffect
  }, [pathname]);

  return (
    <Switch>
      {/* Set the routes inside the switch */}
      {menuItems}
    </Switch>
  );
};

现在问题来了。并非所有组件都加载。通常会加载最后一个组件,并且当单击不同的路线时,组件不会更改。除非您进入页面并刷新 (F5)。

我在这里缺少什么?是否可以在 react 中创建完整的动态路由和组件?

【问题讨论】:

  • 为什么你的useEffect 依赖于pathname?无论当前路径是什么,路径都是相同的。当前路径与正确 Route 的匹配由 react-router-dom 处理。
  • 我发现我在那里犯了一个错误。我认为我需要检查路径是否已更改,如果是,请重新运行 useEffect。但从下面的答案中,我发现这是绝对错误的。

标签: javascript reactjs react-redux react-router react-hooks


【解决方案1】:

我不确定 100% 发生了什么,但我看到了一个问题:

const [menuItems, setMenuItems] = useState([]);

你是说menuItems 是一个数组。但后来:

import(`../pages/${name}/${page}`).then((result) => {
  // Set the routes inside the useState
  setMenuItems(
    <Route exact={exact} path={path} component={result[page]} />
  );
});

在每次迭代中,您都将菜单项设置为单个 Route 组件。可能你认为你在做的是

const routes = items.map(item => {

  const { page, path, name, exact } = item;

  return import(`../pages/${name}/${page}`).then((result) => {
    <Route exact={exact} path={path} component={result[page]} />
  });

})

setMenuItems(routes)

但这没有任何意义,因为您的map 语句返回了Promise.then 函数。我不完全确定您为什么要在此处动态导入组件。你最好做一个简单的路线映射:

const routes = items.map(item => {

  const { page, path, name, exact } = item;

  return <Route exact={exact} path={path} component={components[page]} />

})

setMenuItems(routes)

其中components 是一个对象,其键是page 的值,其值是实际组件,即:

const components = {
  Suppliers: RenderSuppliers,
  Dashboard: RenderDashboard
}

如果你想让这些组件延迟加载,请使用 react suspense:

const Suppliers = React.lazy(() => import("./Suppliers"))
const Dashboard = React.lazy(() => import("./Dashboard"))

const components = {
  Suppliers,
  Dashboard,
}


const routes = items.map(item => {
  const { page, path, name, exact } = item;

  return (
    <Suspense fallback={<SomeFallbackComponent />}>
      <Route
        exact={exact}
        path={path}
        component={components[page]}
      />
    </Suspense>
  )

})

setMenuItems(routes)

这只是对您的代码可能出现的问题的快速回顾,没有可重现的示例,很难准确地说出。

【讨论】:

  • 好吧,我想这会解决我的问题。我想用动态组件构建动态路由器。我会尝试设置一个代码沙盒(我是一个大三学生,所以对我来说很赤裸裸)
  • 我同意你在这里所说的大部分内容,但我会在不实例化它们的情况下制作组件本身的映射。 Suppliers: Suppliers 而不是 Suppliers: &lt;Suppliers&gt;。如果我编辑你的答案很酷吗?因为children 属性实际上与component 属性的行为不同,并且您不想使用children
  • 是的,继续!让我们合作
【解决方案2】:

Seth 有一些很好的建议,但这里是您可以在仍然使用动态导入的同时清理这些问题的方法。

希望您可以看到您正在使用单个 Route 组件而不是所有组件调用 setMenuItems。每次您setMenuItems 时,您都会覆盖之前的结果,这就是为什么只有最后一个Route 真正起作用的原因——它是唯一存在的!

您的useEffect 取决于pathname,这似乎是您尝试自己进行路由。由于您使用的是react-router-dom,您将在Switch 中包含所有Route 组件并让路由器处理路由。

所以这里实际上不需要任何状态。

您可以在Route 中使用React.lazy 组件导入助手。你需要一个 Suspense 提供程序来围绕整个块使用惰性导入。

我不喜欢您在组件../pages/${name}/${page} 的路径中使用两个变量。为什么不从文件夹的./index.js 导出组件呢?

export const Routes = () => {
  // Dispatch to fetch the menu object from the backend
  const dispatch = useDispatch();
  dispatch(setMenu());

  // Select the menu and items from Redux
  const items = useSelector((state) => state.routes.items);

  return (
    <Suspense fallback={() => <div>Loading...</div>}>
      <Switch>
        {items.map(({ exact, path, name }) => (
          <Route
            key={name}
            exact={exact}
            path={path}
            component={React.lazy(() => import(`../pages/${name}`))}
          />
        ))}
      </Switch>
    </Suspense>
  );
};

有效!

Code Sandbox Link

【讨论】:

  • 您的代码对我不起作用,经过一番调查,我发现其中一个错误是我导出组件的方式。我只有 export const 函数。并且没有导出默认功能。 React lazy 不会(我认为?)导入和解构组件。它必须是默认导出。现在你的代码可以工作了!谢谢!
猜你喜欢
  • 2016-02-24
  • 2022-12-20
  • 2021-02-05
  • 1970-01-01
  • 1970-01-01
  • 2019-10-02
  • 2017-12-23
  • 2018-07-26
  • 1970-01-01
相关资源
最近更新 更多