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.

Handling Events in React

In post Rendering RESTful service with React we created simple UI which render employee list fetched from RESTful service. As part of this post we will extend the same app to support add and delete employees operation.

We will start with updating react-app backend api with add/delete employee operation along with modifying the existing get employee method to return the list of employees following below steps:

Step 1. Define addEmployee method flagged by @PostMapping(“/employee/add”) which will add employee in a class level employee list:

@PostMapping("/employee/add")
public List<Employee> addEmployee(final @RequestBody Employee employee) {
	System.out.println("Adding employee with name : " + employee.getName());
	if(employee.getName() != null && employee.getName().length() > 0)
              employeeList.add(new Employee(employeeList.size(), employee.getName(), "IT"));
	return employeeList;
}

Step 2. Define deleteEmployee method flagged by @PostMapping(“/employee/delete”) which will delete employee from a class level employee list matching the name of an employee, as follows:

@PostMapping("/employee/delete")
public List<Employee> deleteEmployee(final @RequestBody Employee employee) {
	System.out.println("Deleting employee with name : " + employee.getName());
	final Optional<Employee> optional = employeeList.stream().filter(e -> e.getName().equalsIgnoreCase(employee.getName())).findAny();
	 if(optional.isPresent()){
		employeeList.remove(optional.get());
	 }
	return employeeList;
}

Eventually, ReactAppApplication.java should look like:

@SpringBootApplication
@RestController
public class ReactAppApplication {

	final List<Employee> employeeList = new ArrayList<>();
	
	public static void main(String[] args) {
		SpringApplication.run(ReactAppApplication.class, args);
	}

	@GetMapping("/employee/get")
	public List<Employee> get() {
		return employeeList;
	}
	
	@PostMapping("/employee/add")
	public List<Employee> add(final @RequestBody Employee employee) {
		System.out.println("Adding employee with name : " + employee.getName());
		if(employee.getName() != null && employee.getName().length() > 0)
		 employeeList.add(new Employee(employeeList.size(), employee.getName(), "IT"));
		return employeeList;
	}
	
	@PostMapping("/employee/delete")
	public List<Employee> delete(final @RequestBody Employee employee) {
		System.out.println("Deleting employee with name : " + employee.getName());
		final Optional<Employee> optional = employeeList.stream().filter(e -> e.getName().equalsIgnoreCase(employee.getName())).findAny();
		if(optional.isPresent()){
			employeeList.remove(optional.get());
		}
		return employeeList;
	}
}

Step 3: Define addEmployee method/handler in ReactApp component which make a POST call with an employee name as a payload to the addEmployee method we just defined in our controller, as follows:

/Users/ArpitAggarwal/react-app/app/components/react-app.jsx

addEmployee(employeeName){

		let _this = this;
		this.Axios.post('/add', {
        	name: employeeName
         })
		  .then(function (response) {
		    console.log(response);
		    _this.setState({employees: response.data});
		  })
		  .catch(function (error) {
		    console.log(error);
		  });
}

Step 4: Bind addEmployee handler in the constructor of ReactApp component:

constructor(props) {
		super(props);
		this.state = {employees: []};
		this.addEmployee = this.addEmployee.bind(this);
		this.Axios = axios.create({
		    baseURL: "/employee",
		    headers: {'content-type': 'application/json', 'creds':'user'}
		});
}

Step 5: Render the child component – AddEmployee as part of ReactApp component render method, passing addEmployee handler as react props to establish the parent child communication:

render() {
		return (
				<div>
				  <AddEmployee addEmployee={this.addEmployee}/>
				  <EmployeeList employees={this.state.employees}/>
		        </div>
		)
}

Step 6: Create add-employee component inside component directory, as follows:

cd react-app/app/components/
touch add-employee.jsx

And copy the following content:

react-app/app/components/add-employee.jsx

import React, { Component, PropTypes } from 'react'

export default class AddEmployee extends React.Component {

  render(){
    return (
       <div>
          <input type = 'text' ref = 'input' />
          <button onClick = {(e) => this.handleClick(e)}>
             Add Employee
          </button>
       </div>
    )
  }
  handleClick(e) {
     const node = this.refs.input
     const text = node.value.trim()
     console.log(text);
     this.props.addEmployee(text)
     node.value = ''
  }
}

handleClick(e) function defined above is called on Add Employee button click which will further call addEmployee handler defined in ReactApp using props.

With all this in place, our react-app can perform add employee operation. Next we will extend the same to support delete employee operation, following further steps.

Step 7: Define deleteEmployee handler and bind it in ReactApp in the same way we did for addEmployee handler:

/Users/ArpitAggarwal/react-app/app/components/react-app.jsx

constructor(props) {
		super(props);
		this.state = {employees: []};
		this.addEmployee = this.addEmployee.bind(this);
		this.deleteEmployee = this.deleteEmployee.bind(this);
		this.Axios = axios.create({
		    baseURL: "/employee",
		    headers: {'content-type': 'application/json', 'creds':'user'}
		});
}

deleteEmployee(employeeName){
        let _this = this;
        this.Axios.post('/delete', {
        	name: employeeName
          })
          .then(function (response) {
        	_this.setState({employees: response.data});
            console.log(response);
          })
          .catch(function (error) {
            console.log(error);
          });
}

Step 8: Pass deleteEmployee handler to the EmployeeList component which will further pass it to it’s child container:

render() {
		return (
				<div>
				  <AddEmployee addEmployee={this.addEmployee}/>
				  <EmployeeList employees={this.state.employees} deleteEmployee={this.deleteEmployee}/>
		        </div>
			)
	}

Eventually, ReactApp component should look like:

'use strict';
const React = require('react');
var axios = require('axios');

import EmployeeList from './employee-list.jsx'
import AddEmployee from './add-employee.jsx'

export default class ReactApp extends React.Component {

	constructor(props) {
		super(props);
		this.state = {employees: []};
		this.addEmployee = this.addEmployee.bind(this);
		this.deleteEmployee = this.deleteEmployee.bind(this);
		this.Axios = axios.create({
		    baseURL: "/employee",
		    headers: {'content-type': 'application/json', 'creds':'user'}
		});
	}

	componentDidMount() {
		let _this = this;
		this.Axios.get('/get')
		  .then(function (response) {
		     console.log(response);
		    _this.setState({employees: response.data});
		  })
		  .catch(function (error) {
		    console.log(error);
		  });
	}
	
	addEmployee(employeeName){

		let _this = this;
		this.Axios.post('/add', {
        	name: employeeName
         })
		  .then(function (response) {
		    console.log(response);
		    _this.setState({employees: response.data});
		  })
		  .catch(function (error) {
		    console.log(error);
		  });
    }
    
    deleteEmployee(employeeName){
        let _this = this;
        this.Axios.post('/delete', {
        	name: employeeName
          })
          .then(function (response) {
        	_this.setState({employees: response.data});
            console.log(response);
          })
          .catch(function (error) {
            console.log(error);
          });
    }
	render() {
		return (
				<div>
				  <AddEmployee addEmployee={this.addEmployee}/>
				  <EmployeeList employees={this.state.employees} deleteEmployee={this.deleteEmployee}/>
		        </div>
			)
	}
}

Step 9: Update EmployeeList component to pass the deleteEmployee handler to it’s child component – Employee by importing it along with the change in render method to have a Delete 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>
					</tr>
					{employees}
				</tbody>
			</table>
		)
	}
}

Step 10: Update Employee component to render – DeleteEmployee component passing the deleteEmployee handler:

const React = require('react');
import DeleteEmployee from './delete-employee.jsx'

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>
			</tr>
		)
	}
}

Step 11: Create delete-employee component inside component directory:

cd react-app/app/components/
touch delete-employee.jsx

And copy the following content:

react-app/app/components/delete-employee.jsx

import React, { Component, PropTypes } from 'react'

export default class DeleteEmployee extends React.Component {
  render(){
    return (
          <button onClick = {(employeeName) => this.handleDelete(employeeName)}>
             Delete
          </button>
    )

}
  handleDelete(employeeName) {
   this.props.deleteEmployee(employeeName);
  }
}

handleDelete(employeeName) function defined above is called on Delete button click which will further call deleteEmployee handler defined in ReactApp using props.

With all in place directory structure should look like:

Screen Shot 2017-05-15 at 8.38.57 pm

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

Screen Shot 2017-05-15 at 8.41.06 pm

Complete source code is hosted on github.

Rendering RESTful service with React

Looking at the popularity of React, I thought of learning it and creating a simple UI which will render data from RESTful service.

With this post, I will try to replicate the steps I followed while writing it along with references. Before starting, let me give you a brief introduction about React.

What is React?

React is a declarative, efficient, and flexible JavaScript library for building user interfaces. It uses virtual DOM which improve apps performance since JavaScript virtual DOM is faster than the regular DOM with a limitation that it only covers view layer of the app so you still need to choose other technologies to get a complete tooling set for development.

Now, lets’ start with creating react-app running on port 8080, following below steps:

Step 1: Go to start.spring.io and create a new project react-app adding the Thymeleaf starters, based on the following image:

Screen Shot 2017-05-06 at 2.53.30 pm.png
Step 2: Edit ReactAppApplication.java to add a method which returns a list of employee, as follows:

package com.arpit.react.app;

import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class ReactAppApplication {

	public static void main(String[] args) {
		SpringApplication.run(ReactAppApplication.class, args);
	}

	@GetMapping("/employee/get")
	public List<Employee> get() {
		List<Employee> employeeList = new ArrayList<>();
		employeeList.add(new Employee(1, "Arpit", "IT"));
		employeeList.add(new Employee(2, "Sanjeev", "IT"));
		return employeeList;
	}
}

@Controller
class IndexPageController {

	@GetMapping(value = "/")
	public String index() {
		return "index";
	}
}

final class Employee {

	private int id;
	private String name;
	private String department;

	public Employee() {

	}

	public Employee(final int id, final String name, final String department) {
		this.id = id;
		this.name = name;
		this.department = department;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDepartment() {
		return department;
	}

	public void setDepartment(String department) {
		this.department = department;
	}
}

IndexPageController define index() method flagged by @GetMapping(value = “/”) to support the / route. It returns index as the name of the template, which Spring Boot’s autoconfigured view resolver will map to src/main/resources/templates/index.html.

Step 3: Define an HTML template src/main/resources/templates/index.html with the following content:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<meta charset="UTF-8" />
<title>React with Spring REST</title>
</head>
<body>
	<div id="react"></div>
	<script src="package/script.js"></script>
</body>
</html>

Step 4: Move to react-app directory and run command: mvn spring-boot:run. Once running open http://localhost:8080/employee/get which will give you the list of employees we are going to render on UI built with React.

Step 5: Next we will add frontend-maven-plugin in pom.xml  to install Node and NPM locally for the react-app followed by running Webpack  build, as follows:

<plugin>
				<groupId>com.github.eirslett</groupId>
				<artifactId>frontend-maven-plugin</artifactId>
				<version>1.2</version>
				<configuration>
					<installDirectory>target</installDirectory>
				</configuration>
				<executions>
					<execution>
						<id>install node and npm</id>
						<goals>
							<goal>install-node-and-npm</goal>
						</goals>
						<configuration>
							<nodeVersion>v4.4.5</nodeVersion>
							<npmVersion>3.9.2</npmVersion>
						</configuration>
					</execution>
					<execution>
						<id>npm install</id>
						<goals>
							<goal>npm</goal>
						</goals>
						<configuration>
							<arguments>install</arguments>
						</configuration>
					</execution>
					<execution>
						<id>webpack build</id>
						<goals>
							<goal>webpack</goal>
						</goals>
					</execution>
				</executions>
</plugin>

Step 6: Execute npm init in the root directory to create package.json in which we specify all the dependencies required to build our react-app like React, React DOM, Webpack, Babel Loader, Babel Core, Babel Preset: ES2015, Babel Preset: React, as follows:

$ cd react-app
$ touch npm init

Copy the following content:

{
  "name": "react-app",
  "version": "1.0.0",
  "description": "Rendering RESTful service with React",
  "repository": {
    "type": "git",
    "url": "git@github.com:arpitaggarwal/react-app.git"
  },
  "keywords": [
    "rest",
    "spring",
    "react"
  ],
  "author": "Arpit Aggarwal",
  "dependencies": {
    "axios": "^0.16.1",
    "react": "^15.3.2",
    "react-dom": "^15.3.2",
    "webpack": "^1.12.2"
  },
  "scripts": {
    "watch": "webpack --watch -d"
  },
  "devDependencies": {
    "babel-core": "^6.18.2",
    "babel-loader": "^6.2.7",
    "babel-polyfill": "^6.16.0",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0"
  }
}

Step 7: Next we will create webpack.config.js to configure webpack, as follows:

$ cd react-app
$ touch webpack.config.js

Copy the following content:

var path = require('path');

module.exports = {
    entry: './app/main.js',
    cache: true,
    debug: true,
    output: {
        path: __dirname,
        filename: './src/main/resources/static/package/script.js'
    },
    module: {
        loaders: [
            {
                test: path.join(__dirname, '.'),
                exclude: /(node_modules)/,
                loader: 'babel',
                query: {
                    cacheDirectory: true,
                    presets: ['es2015', 'react']
                }
            }
        ]
    }
};

entry option specified above is the entry point for the bundle.
cache option specified above Cache generated modules and chunks to improve performance for multiple incremental builds.
output option specified above tell Webpack how to write the compiled files to disk.

For more configuration options you can explore here.

Step 8: Next we will create entry point for the webpack which is react-app/app/main.js, as:

$ cd react-app
$ mkdir app
$ cd app
$ touch main.js

Copy the following content:

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

import ReactApp from './components/react-app.jsx'

ReactDOM.render(
		<ReactApp />,
	document.getElementById('react')
)

React is the main library from Facebook for building the app.
ReactDOM provides DOM-specific methods that can be used at the top level.
ReactApp is the top level container for all React components.

Let’s define ReactApp along with it’s child components, as:

$ cd react-app/app/
$ mkdir components
$ cd components
$ touch react-app.jsx employee-list.jsx employee.jsx 

react-spring/app/components/react-app.jsx

'use strict';
const React = require('react');
var axios = require('axios');

import EmployeeList from './employee-list.jsx'

export default class ReactApp extends React.Component {

	constructor(props) {
		super(props);
		this.state = {employees: []};
		this.Axios = axios.create({
		    baseURL: "/employee",
		    headers: {'content-type': 'application/json', 'creds':'user'}
		});
	}

	componentDidMount() {
		let _this = this;
		this.Axios.get('/get')
		  .then(function (response) {
		     console.log(response);
		    _this.setState({employees: response.data});
		  })
		  .catch(function (error) {
		    console.log(error);
		  });
	}

	render() {
		return (
				<div>
				  <EmployeeList employees={this.state.employees}/>
		        </div>
			)
	}
}

react-spring/app/components/employee-list.jsx

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}/>
		);
		
		return (
			<table>
				<tbody>
					<tr>
						<th>ID</th>
						<th>Name</th>
						<th>Department</th>
					</tr>
					{employees}
				</tbody>
			</table>
		)
	}
}

react-spring/app/components/employee.jsx

const React = require('react');

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>
			</tr>
		)
	}
}

With all this in place, your directory structure should look like:

Screen Shot 2017-05-06 at 4.49.26 pm

Now re-run the application and visit http://localhost:8080.

Complete source code is hosted on github.