Passing props to children components in React-Router without Redux

React-Router is a library that allows you to build a React Single Page App with multiple “views” and changing URLs. At the top most component of your React app, you can specify the layout, including the hierarchy, of your routes.

For example: (Note that I am using ES6 and JSX syntax.)

class App extends Component {

render() {
// App just defines the routes
return(
<Router history={browserHistory}>
<Route path="/" component={Home}>
<Route path="/signup" component={Signup}/>
<Route path="/login" component={Login}/>
<Route path="/logout" component={Logout}/>
<Route path="/dashboard" component={Dashboard}>
<Route path="/dashboard/configForm" component={ConfigForm}/>
<Route path="/dashboard/configDetail/:id" component={ConfigDetail}/>
</Route>
</Route>
</Router>
);
}
}

In the code above, the “signup”, “login”, “logout”, and “dashboard” are child routes of the “/” root route. By the same reasoning, “/dashboard/configForm” and “/dashboard/configDetail/:id” are child routes of the “/dashboard” route. Note that each route has a corresponding “component” tied to it.

Suppose that in my “/dashboard” route and thus Dashboard component, I want to pass down some props to my children components, in this case, ConfigForm and ConfigDetail. However, my hierarchical relationship is defined all the way up in my topmost level component, which is out of Dashboard’s reach.

One way to go about it is using Redux which is a predictable state container for any Javascript application. In particular, the React-Redux library allows you to bind the actions you create in Redux to your React components. Then the React-Redux-Router library helps you keep your URLs in sync. For me, all these Redux libraries were a bit over my head and I wondered if there was an alternative way.

The answer is using React.cloneElement() in my Dashboard component, which allows you to clone your children component with the option to add in additional props. Usually, in React-Router, {this.props.children} is used as a placeholder for rending the children components or “views”.

render() {
// Note: {this.props.children} is a placeholder container for displaying the content of different children routes
return(
<div>
{this.props.children}
</div>
);
}

Using the React.cloneElement() method, I am able to redefine the children components while adding in additional props, in this case, an event handler function called handleConfigFormSubmit. Now I access my redefined children clones with {childrenWithMoreProps} instead of {this.props.children} in my render(). And my children components can now have access to the additional props!

render() {
// Note: {childrenWithMoreProps} is a placeholder container for displaying the content of different children routes

var childrenWithMoreProps = React.Children.map(this.props.children, (child) => {
if(child.type === ConfigForm) {
return React.cloneElement(child, {
handleConfigFormSubmit: this.handleConfigFormSubmit
});
} else {
return child;
}
});
return(
<div>
<h2>Dashboard</h2>
<p>My Config Files</p>
<ul>
{this.state.configs.map((config, index) => {
var href = "/dashboard/configDetail/" + config._id;
return <li key={index}><Link to={href}>Config # {config._id}</Link></li>;
})}
</ul>
<Link to="/dashboard/configForm">Add a Config File</Link>
{childrenWithMoreProps}
</div>
);
}
}

For large-scale React apps, Redux is probably still the way to go in order to manage complex state. However, for a simpler React app using React-Router, the React.cloneElement() method may be sufficient for your needs. Now go forth and build!

A single golf clap? Or a long standing ovation?

By clapping more or less, you can signal to us which stories really stand out.