Routing in React

In post Handling Events in React we extended react-app which we built as part of Rendering RESTful service with React to support add and delete employees operation.

Now as part of this post we will add Routing capabilities to it using react-router, by defining an additional column Details as a Link, click of which take you to the details of the employee selected, following below steps:

Step 1. Install react-router-dom as a dependency:

npm install react-router-dom --save

Step 2: Update react-handling-events/app/main.js to import HashRouter, Route and Switch from react-router-dom along with EmployeeDetail component which we will define later in the post.

import { HashRouter, Route, Switch } from 'react-router-dom';
import EmployeeDetail from './components/employee-detail.jsx'

Step 3: Instead of rendering the ReactApp component directly, we will use HashRouter and Route to render it:

ReactDOM.render(
	<HashRouter>
	 <Switch>
	    <Route exact path="/" component={ReactApp}/>
        <Route exact path="/employee/:id" component={EmployeeDetail}/>
     </Switch>
    </HashRouter>,
   document.getElementById('react')
)

Eventually, react-handling-events/app/main.js should look like:

'use strict';
const React = require('react');
const ReactDOM = require('react-dom')

import { HashRouter, Route, Switch } from 'react-router-dom';
import ReactApp from './components/react-app.jsx'
import EmployeeDetail from './components/employee-detail.jsx'

ReactDOM.render(
    <HashRouter>
      <Switch>
        <Route exact path="/" component={ReactApp}/>
        <Route exact path="/employee/:id" component={EmployeeDetail}/>
      </Switch>
    </HashRouter>,
  document.getElementById('react')
)

HashRouter tag specified above puts route information into the hash of the URL (after the #). I preferred HashRouter over BrowserRouter because to use BrowserRouter web server must be ready to handle real URLs. When the app first loads at / it will probably work, but as the user navigates around and then hits refresh at /employee/1 web server will get a request to /employee/1 then I have to handle that URL and include JavaScript application in the response.

Switch tag specified above renders the first child Route that matches the location.

As we defined two Route tag above, let’s understand what each one of them signify.

<Route exact path=/ component={ReactApp}/renders ReactApp component when the app loads at / .

<Route exact path=/employee/:id component={EmployeeDetail}/renders EmployeeDetail component when the app loads at /employee/{id} where id is a dynamic path variable.

Step 4: Create employee-detail.jsx component inside component directory, as follows:


cd react-handling-events/app/components/
touch employee-detail.jsx

And copy the below content:

import React, { Component } from 'react'

export default class EmployeeDetail extends Component {
  render(){
    if(this.props.match !== undefined && this.props.match.params !== undefined ){
      return(
        <div>
          <table style={{border: '1px solid black'}}>
            <tbody><tr>
              <td>Id</td>
              <td>Name</td>
              <td>Department</td>
            </tr>
            <tr>
              <td>{this.props.match.params.id}</td>
              <td>{this.props.location.query.employee.name}</td>
              <td>{this.props.location.query.employee.department}</td>
            </tr>
          </tbody>
        </table>
      </div>
    );
  }
  return (null);
}
}

{this.props.match.params.id} specified above is to access route params in the component.

{this.props.location.query.employee.name} specified above is to access query params in the component which React-router injects as a location property.

Step 5: Update EmployeeList component render method to have an additional Details column:

const React = require('react');
import Employee from './employee.jsx'

export default class EmployeeList extends React.Component{
    
    render() {
		var employees = this.props.employees.map((employee, i) =>
			<Employee key={i} employee={employee} deleteEmployee={() => this.props.deleteEmployee(employee.name)}/>
		);
		
		return (
			<table>
				<tbody>
					<tr>
						<th>ID</th>
						<th>Name</th>
						<th>Department</th>
						<th>Delete</th>
						<th>Details</th>
					</tr>
					{employees}
				</tbody>
			</table>
		)
	}
}

Step 6: Update Employee component to import Link from react-router-dom along with render method to have an additional column Details as a Link, click on which take you to the details of the employee:

const React = require('react');
import DeleteEmployee from './delete-employee.jsx'
import { Link } from 'react-router-dom';

export default class Employee extends React.Component{
	render() {
		return (
			<tr>
				<td>{this.props.employee.id}</td>
				<td>{this.props.employee.name}</td>
				<td>{this.props.employee.department}</td>
				<td><DeleteEmployee deleteEmployee={this.props.deleteEmployee}/></td>
				<td><Link to={{ pathname:'/employee/'+this.props.employee.id, query: {employee: this.props.employee } }}>Details</Link></td>
			</tr>
		)
	}
}

With all in place directory structure should look like:

Screen Shot 2017-05-28 at 12.27.17 am

Now re-run the application and visit http://localhost:8080it should look like as shown in below screenshot, once you add few employee.

Screen Shot 2017-05-28 at 12.30.17 am
Complete source code is hosted on github.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s