2

I can't seem to figure out how to print child routes in React Router v5. Here is how I have setup my application.

1) index.jsx

ReactDOM.render(
<Provider store={store}>
  <IntlProvider defaultLocale="en" locale="en" messages={messages}>
    <ThemeProvider theme={theme}>
      {Routes()}
    </ThemeProvider>
  </IntlProvider>
</Provider>,
root,

);

2) Routes.jsx

export default function Routes() {
  return (
    <ConnectedRouter history={history}>
      <Switch>
        <Route path="/welcome" component={App} />
        <Route component={UnknownPage} />
      </Switch>
   </ConnectedRouter>
  );
}

3) App.jsx

const App = ({ location }) => (
  <div>
    <DialogMount />
    <RefreshSession />
    <Masthead />
    <Navigation />
    <PageWrapper>
      <NavTabs location={location} />
      <ContentWrapper>
        <Alert />
        <Switch>
          {generateRoutes(routesConfig)}
        </Switch>
      </ContentWrapper>
    </PageWrapper>
  </div>
);

4) generateRoutes method

export const generateRoutes = (routes = []) => Object.values(routes).map((route) => {
  if (route.redirect) {
    return [];
  } else if (route.children) {
    return (
      <Route key={route.path} path={route.path}>
        <Switch>
          {generateRoutes(route.children)}
        </Switch>
      </Route>
    );
  }
  return <Route key={route.path} path={route.path} component={route.component} />;
}).reduce((navigation, route) => navigation.concat(route), []);

5) routesConfig

const routesConfig = {
  parent: {
    path: 'parent',
    name: 'parent',
    children: {
      child1: {
        path: 'child1',
        name: 'child1',
        component: Child1,
      },
    },
  },
};

The problem is, from my App.jsx, everything until the NavTabs is being rendered. Just the routed part of it is not being rendered. I know I am missing something very silly here but can't seem to figure out.

Any help is appreciated.

Edit after Shubham's answer:

I made the changes, but still facing the same issue. However instead of

render={props => <route.component {...props} />}

I used

children={props => <route.component {...props} />}.

This seems to be loading the components, but now I see errors as such:

Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of `Licensing`.
    at createFiberFromTypeAndProps (react-dom.development.js:23965)
    at createFiberFromElement (react-dom.development.js:23988)
    at createChild (react-dom.development.js:13628)
    at reconcileChildrenArray (react-dom.development.js:13900)
    at reconcileChildFibers (react-dom.development.js:14305)
    at reconcileChildren (react-dom.development.js:16762)
    at updateHostComponent (react-dom.development.js:17302)
    at beginWork (react-dom.development.js:18627)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
Vinod Bhavnani
  • 2,185
  • 1
  • 16
  • 19

1 Answers1

1

The issue is happening because unless you specify the nested routes within the rendered component itself, you need to provide the entire pathname to it.

The solution is to pass on a prefix to append before the pathname. Also we need a trailing /

const generateRoutes = (routes = [], prefix = "") =>
  Object.values(routes)
    .map(route => {
      console.log(prefix);
      if (route.redirect) {
        return [];
      } else if (route.children) {
        return (
          <Route key={route.path} path={`${prefix}/${route.path}`}>
            <Switch>
              {generateRoutes(route.children, prefix + "/" + route.path)}
            </Switch>
          </Route>
        );
      }
      return (
        <Route
          path={`${prefix}/${route.path}`}
          key={route.path}
          render={props => <route.component {...props} />}
        />
      );
    })
    .reduce((navigation, route) => navigation.concat(route), []);

Working DEMO

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Hi Shubham, thanks for the answer. Can you please check my update after your answer? – Vinod Bhavnani Apr 28 '20 at 13:54
  • Hi Vinod the component needs to be imported while rendering. Either you have it in routeConfig file or create an import mapping in generateRoutes – Shubham Khatri Apr 28 '20 at 14:06
  • Hi Shubham, I must let you know that by doing all this I am just updating react and all other packages to the latest version. Earlier all this used to work. I have made sure that default exports are imported without { } and normal exports are imported with {}. The same setup used to work before – Vinod Bhavnani Apr 28 '20 at 14:12
  • @VinodBhavnani The usage of react-router has changed with respect to nested routes in v4 that is why the problem is happening. From version 4 the idea to nest routes is based on writing nested routes in child component instead of as children to Route – Shubham Khatri Apr 28 '20 at 14:50
  • Also the component needs to be imported where routesConfig is defined. I updated the codesandbox a bit to show how imports would work. Also please use render instead of children to Route as render is more optimal and isn't called each time a route is changed – Shubham Khatri Apr 28 '20 at 14:54
  • Hi Shubham, thanks for bearing with me. In my case render doesn't work, I don't understand why. Is it because I am using connected-router? My application uses redux and I am not sure if I should be using connected-router or not. – Vinod Bhavnani Apr 28 '20 at 22:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/212740/discussion-between-vinod-bhavnani-and-shubham-khatri). – Vinod Bhavnani Apr 28 '20 at 23:01
  • Hi Shubham, it was not the router's problem. It was an issue with connected-react-router. I was using two separate copies of the history object. One which was attched to the middleware and a separate one which I was sending as a prop to the router itself. Apart from that my Saga was listening to the wrong location change event. Connected-react-router has inbuilt LOCATION_CHANGE to which I had to subscribe. Also changing some property names on the payload of the location change action. Took a little bit of doing, but it works now. – Vinod Bhavnani Apr 30 '20 at 18:06
  • Hi Shubham, still need your help with this.Have some time? – Vinod Bhavnani May 08 '20 at 10:46
  • Let me know the issue. – Shubham Khatri May 08 '20 at 10:47
  • Editing the question – Vinod Bhavnani May 08 '20 at 10:50
  • Lets continue this discussion in chat. https://chat.stackoverflow.com/rooms/213409/routes-chat – Shubham Khatri May 08 '20 at 10:51
  • Hi Shubham, have a few minutes? – Vinod Bhavnani May 12 '20 at 12:30