Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically Define ReactJS Routes from JSON

I have a global JSON configuration that has a variety of configuration data, including route names and the components that I'd like to be rendered for the route. I'd like to dynamically build my routes from this JSON configuration. I'm currently able to dynamically generate the route, however, since the route components are Strings in the JSON file, React doesn't like them and throws the error below. Any suggestions for accomplishing this goal?

warning.js:44 Warning: Unknown props `history`, `location`, `params`, `route`, `routeParams`, `routes` on <Dashboard> tag. Remove these props from the element. For details, see https://f b.me/react-unknown-prop
    in Dashboard
    in RouterContext
    in Router

Relevant JSON snippet:

{
    "routes" : [
        {
            "route_name"              : "home",
            "path"                   : "/",
            "visible_in_menu"         : true,
            "menu_title"              : "Home",
            "menu_font_awesome_icon"  : "fa fa-home",
            "component"              : "App",
            "child_routes"            : null
        },
        {
            "route_name"              : "analytics",
            "path"                   : "/analytics",
            "visible_in_menu"         : true,
            "menu_title"              : "Analytics",
            "menu_font_awesome_icon"  : "fa fa-bar-chart",
            "component"              : "Dashboard",
            "template"                : null,
            "child_routes" : [
                {
                    "route_name"      : "analytics_daily" ,
                    "path"           : "/daily",
                    "visible_in_menu" : true,
                    "menu_title"      : "Daily",
                    "component"      : "Dashboard"
                }
            ]
        }
    ]
}

Code to fetch config and dynamically build the routes:

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Link, browserHistory } from 'react-router'
import $ from 'jquery';

import App from './App'; 
import Dashboard from './Dashboard';
import SidebarMenu from './SidebarMenu';

import './index.css';

$.get("/routes_menu_config.json", bootstrapApplication);

function bootstrapApplication(config){

    var dynamicRoutesJSX = buildDynamicRoutes(config);

    // Add dynamic routes to app
    ReactDOM.render((
      <Router history={browserHistory}>
        {dynamicRoutesJSX}
      </Router>
    ), document.getElementById('root'));

    // Render sidebar menu
    ReactDOM.render(
        <SidebarMenu />,
        document.getElementById('menu')
    ); 

}

function buildDynamicRoutes(config){

    var routeJSX = [];

    for (let [index, route] of config.routes.entries()) {
      routeJSX.push(<Route path={route.path} components={route.component} key={index}/>);       
    }

    return routeJSX;
}

I also tried the plain object instantiation of routes instead of using JSX, but it produces the same error:

$.get("/routes_menu_config.json", bootstrapApplication);

function bootstrapApplication(config){

    var dynamicRoutesJSX = buildDynamicRoutes(config);

    // Add dynamic routes to app
    ReactDOM.render(<Router history={browserHistory} routes={dynamicRoutesJSX} 

    // Render sidebar menu
    ReactDOM.render(
        <SidebarMenu />,
        document.getElementById('menu')
    ); 

}


function buildDynamicRoutes(config){

    var routes = [];

    // Iterate parent routes from config
    for (var i=0; i<config.routes.length; i++) {

        var configParentRouteDefinition = config.routes[i];

        // Create parent route definition
        var parentRouteDefinition = {
            path : configParentRouteDefinition.path,
            component: configParentRouteDefinition.component
        };

        // If there are child routes, add them to the definition
        if(configParentRouteDefinition.child_routes){

            // Track child routes
            var childRoutes = [];

            // Iterate child routes, generate definition
            for(var j=0; j<configParentRouteDefinition.child_routes.length; j++){

                var configChildRouteDefinition = configParentRouteDefinition.child_routes[j]


                var childRouteDefinition = {
                    path : configChildRouteDefinition.path,
                    component: configChildRouteDefinition.component         
                };

                childRoutes.push(childRouteDefinition);
            }

            // Ad the definition to the parent route definition
            parentRouteDefinition["childRoutes"] = childRoutes;

        }

        routes.push(parentRouteDefinition);

    }

    return routes;

}
like image 659
ph34r Avatar asked Aug 11 '16 17:08

ph34r


2 Answers

You need to create a registry of components so that you can look up component references from their name:

import App from './App'; 
import Dashboard from './Dashboard';

const componentRegistry = {
    "App": App,
    "Dashboard": Dashboard
}

<Route path={route.path} components={componentRegistry[route.component]} key={index}/>;
like image 129
Aaron Beall Avatar answered Nov 14 '22 22:11

Aaron Beall


If you use e.g. Webpack it is possible without import all components to have result you want. However you have to pass path to the component rather the component's name. like so:

json file:
{"nav": [
{
  "label": "HOME",
  "route": {
    "url": "/",
    "exact": true
  },
  "component": {
    "path": "home",
    "name": "home"
  }
}}
createRoutes() {
    return this.props.data.nav.map((item: any, index: number) => {
        let DynamicComponent = require('../../' + item.component.path + '/' + item.component.name).default;
        return <Route key={index} path={item.route.url}
                      exact={item.route.exact}
                      render={() => (<DynamicComponent key={item.component.name}/>)}/>
    });
}
like image 24
proti Avatar answered Nov 14 '22 20:11

proti