Search code examples
javascriptreactjsreact-routernested-routesconnected-react-router

Can't print child components of nested routes on React Router v5


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)

Solution

  • 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