【问题标题】:React Router - routes not working using a FragmentReact Router - 路由不使用片段
【发布时间】:2021-07-12 09:06:30
【问题描述】:

React 和 javascript 相对较新,但正在尝试组织一个项目,其中一些路由从不同的常量中提取,因此我可以在另一个文件中使用它们,以便有人可以在单独的模块上开发和测试,而无需完整的应用程序(伪微前端类型的东西)。这可能是过度设计,但基本上,当我将路由放入 const 并从其他地方引用它们时,我的 const 引用下方的任何路由都只会加载空白页面而不是组件或 html。

任何指导将不胜感激。

(react 17.0.2 和 react-router-dom 5.1.2)

routes.js):

import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
import * as Yup from 'yup';
import Location from 'app-location';
import * as profile from '@profile-views';
import * as view from "modules/app/views";
...

const PROFILE_ROOT = `/profile`;

const userId = Yup.string();

/* Profile Routes */
export const Profile = new Location(
    `${PROFILE_ROOT}/:userId`, 
    { 
      userId: userId.required()
    }
  );
export const ProfileContactInfo = new Location(
  `${Profile.path}/contact`, 
  { userId: userId.required()}, 
);
export const ProfileCalendar = new Location(
  `${Profile.path}/calendar`, 
  { userId: userId.required()}, 
);
...
export const renderRoutes = (
  <>
      {/* everything works if Route components are in Routes() */}
    <Route exact path={Profile.path} component={profile.Timeline}/>
    <Route path={ProfileCalendar.path} component={profile.Calendar}/>
    <Route path={ProfileContactInfo.path} component={profile.ContactInfo}/>
  </>
)

export default function Routes() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/login" component={view.Login} />
        { renderRoutes }
        {/* EVERYTHING ABOVE WORKS */}
        {/* EVERYTHING BELOW renderRoutes DOES NOT WORK */}
        {/* everything below works if i remove renderRoutes */}
        {/* everything works if i copy Routes from renderRoutes here */}
        <Route path="/create-user" component={view.CreateUser} />
        <Route path="/404" component={() => <h1>Not Found!</h1>} />
        <Redirect to="/404" />
      </Switch>
    </BrowserRouter>
  );
}

App.js:

import Routes from "./routes";
...
function App() {
    return (
        <UserProvider>
            <Routes />
        </UserProvider>
    );
}

【问题讨论】:

  • 您能否编辑帖子并提供 Profile.path 的值,例如您已导入的内容,以便我们更好地理解问题

标签: reactjs react-router


【解决方案1】:

在这里找到答案:https://github.com/ReactTraining/react-router/issues/5785

Switch 组件不喜欢 React Fragment 作为子组件。解决方法似乎是向 Switch 添加一个包装组件,以删除片段。

根据链接更新了以下内容,一切正常。

import React, { Fragment } from 'react';

...

export const FragmentSupportingSwitch = ({children}) => {
  const flattenedChildren = [];
  flatten(flattenedChildren, children);
  return React.createElement.apply(React, [Switch, null].concat(flattenedChildren));
}

function flatten(target, children) {
  React.Children.forEach(children, child => {
    if (React.isValidElement(child)) {
      if (child.type === Fragment) {
        flatten(target, child.props.children);
      } else {
        target.push(child);
      }
    }
  });
}

...

export default function Routes() {
  return (
    <BrowserRouter>
      <FragmentSupportingSwitch>
        <Route path="/login" component={view.Login} />
        { renderRoutes }
        <Route path="/create-user" component={view.CreateUser} />
        <Route path="/404" component={() => <h1>Not Found!</h1>} />
        <Redirect to="/404" />
      </FragmentSupportingSwitch>
    </BrowserRouter>
  );
}

【讨论】:

    【解决方案2】:

    问题与您在变量渲染路由&lt;&gt;&lt;/&gt; Switch 中使用的 React Fragment 有关,您包装的 Switch 仅适用于其正下方的第一级组件。我们不能遍历整棵树。解决它的一种方法是删除 switch 并仅使用路由, switch 的作用是它只会返回一个组件,因此删除它或仅将 switch 包装在 renderRoutes 这样的周围

    第一个解决方案

    <BrowserRouter>
    <Switch>{renderRoutes()}</Switch>
    <Routes path=/ component={Home}/>
    </BrowserRouter>
    

    另一种方法是实现下面的函数

    第二种解决方案

    你可以使用下面的代码使用两个函数让Fragment在Switch中被支持,通过包装FragmentSupportingSwitch,你可以解决它

    
    
    
    function FragmentSupportingSwitch({ children }) {
      const flattenedChildren = [];
      flatten(flattenedChildren, children);
      return React.createElement.apply(
        React,
        [Switch, null].concat(flattenedChildren)
      );
    }
    
    function flatten(target, children) {
      React.Children.forEach(children, (child) => {
        if (React.isValidElement(child)) {
          if (child.type === React.Fragment) {
            flatten(target, child.props.children);
          } else {
            target.push(child);
          }
        }
      });
    }
    
    
    export default function App() {
      return (
        <div className="App">
          <BrowserRouter>
            <Switch>
              <FragmentSupportingSwitch>
                {commonRoute}
                <Route path="/" component={Home} />
                <Route path="/apple" component={Apple} />
              </FragmentSupportingSwitch>
            </Switch>
          </BrowserRouter>
        </div>
      );
    }
    
    
    

    您可以查看此代码沙箱以供参考

    【讨论】:

      【解决方案3】:

      renderRoutes 函数实际上是它的父亲Route

      <Route path={PROFILE_ROOT} component={RenderRoutes} />
      
      const RenderRoutes = () => (
        <Switch>
          <Route exact path={Profile.path} component={profile.Timeline}/>
          <Route path={ProfileCalendar.path} component={profile.Calendar}/>
          <Route path={ProfileContactInfo.path} component={profile.ContactInfo}/>
        </Switch>
      )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-07-07
        • 2022-07-09
        • 2022-10-16
        • 1970-01-01
        • 2017-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多