Spring Boot and ReactJs

Umair Aslam's Tech Blog
Coding Crackerjack
Published in
14 min readFeb 19, 2018

In this tutorial, I will teach you how to create an application with a ReactJs frontend and Spring MVC backend.

Pre-requisites

This tutorial is a sequel to my earlier tutorial on MERN stack development in which I created an ‘Expense Manager’ application using a React front-end, NodeJs and ExpressJs back-end and a MongoDB database. I will use the front-end code from the afore-mentioned tutorial and bind it with a Spring MVC back-end so it is highly recommend that you check the tutorial out before moving forward.

Please remember that this is NOT a ReactJs or Spring MVC tutorial.

End Goal

We will create an Expense Manager application using Spring MVC at the backend and ReactJs on the frontend. The application will enable the user to add, update, delete and display their expenses. I am using the Windows 10 OS for this tutorial. Let’s get to it.

Step 1: Create the Spring MVC API

Our first step would be to create an API using Spring MVC. We will head to Spring Initializr to quickly bootstrap our Expense Manager API. We will need the Web and MongoDb dependencies for this project.

Bootstrapping our API

A zipped Maven project will be generated once we click Generate Project. We will unzip the file and import it into our IDE. For Spring MVC projects, STS (Spring Tool Suite) and IntelliJ are usually preferred.

Now let’s start building.

Step 2: Create the Expense Document

We will start from the bottom. Create a package and name it com.expenseManager.ExpenseManagerAPI.domain. Inside this package, we create a class and call it Expense. Inside this class, we will design our Expense object. I would like to have an id, a description, an amount, month and year.

Expense.java

We annotate our Expense class with ‘@Document’ annotation to tell Spring that this class represents a document in MongoDb. We also annotate the id property with ‘@Id’ annotation so Spring knows that the Id of Expense document will be stored here. As you may already know that MongoDb generates a default id for each document. When the property annotated with ‘@Id’ has the datatype String, Spring understands that we want to store the default document id generated by MongoDb inside the annotated property. However, if the property annotated with ‘@Id’ has any other datatype, Spring understands that we do not want to use the default document id generated by MongoDb but instead we want to override this behavior by assigning the id manually. In our case, we have given the datatype String to our id property to indicate to Spring that we want to use the default document id.

Step 2: Create the Expense Repository

Create a new package and name it com.expenseManager.ExpenseManagerAPI.repositories. Inside this package, create an interface and call it ExpenseRepository.

We will extend ExpenseRepository with MongoRepository<T, ID>. Like CrudRepository<T,ID>, the MongoRepository<T,ID> gives us access to generic CRUD operations for the specified type (T). In our case the type is Expense class we just created and our id has the datatype String as discussed earlier.

I want our API to be capable of creating, modifying, removing and retrieving all expenses. These three operations can be performed by the methods provided by the MongoRepository interface that our repository interface is extending. However, for more complex operations we would need to declare our own methods. For example, I want to retrieve all the expense from a certain month and/or a certain year. To perform such an operation, we will use Spring Data’s query methods and add the following two methods to our repository interface.

ExpenseRepository.java

Please note that you do NOT need to implement these methods. Spring does it for you.

Step 3: Create the Expense Service

Create a new package and name it com.expenseManager.ExpenseManagerAPI.service. Inside this package, create an interface and call it ExpenseService.

In our interface, we will add the generic CRUD methods and the two retrieval methods we declared in our repository.

ExpenseService.java

Now let’s implement this interface. Create an ExpenseServiceImpl class that will implement this interface. Don’t forget to put the ‘@Service’ annotation on the ExpenseServiceImpl class so Spring knows that this is our Service class.

ExpenseServiceImpl.java

We autowire the ExpenseRepository class into our service class to use the methods declared in the repository class.

Now let’s proceed to creating our ExpenseController class.

Step 4: Create the Expense Controller

Create a new package and name it com.expenseManager.ExpenseManagerAPI.controller. Inside this package, create a class and call it ExpenseController.

Now annotate this class with the ‘@RestController’ annotation to tell Spring that this class is your controller. Also, insert the ‘@RequestMapping’ annotation and specify the desired mapping in the brackets to tell the spring to map the specified requests to this class. I am mapping all the ‘/expense’ requests to this class.

Now we will add some generic methods to perform the CRUD operations.

ExpenseController.java

Step 5: Testing our API

I will use Postman to test the API.

First run the API and launch MongoDb by running mongod.exe.

Now head to Postman. Write http://localhost:8080/expense and select GET from the dropdown. This request will get a list of all the expenses stored in the MongoDb. You should get an empty response. That is because we do not have any expenses yet in the Expense collection.

Test the GET method using Postman

Now let’s add an expense using the POST request and then make this GET request again to see if the API returns the newly inserted expense or not.

Test the POST method using Postman

Play around with your API and test it thoroughly using Postman before moving on to the frontend.

Step 6: Implement the front-end

For front-end I will use the approach I used in the first half of this tutorial.

First of all we will create a basic react application to get us started. To do that we will use the create-react-app package. First install the create-react-app package by running the following command.

npm i -g create-react-app

We will now use create-react-app package to create a new react application.

create-react-app expense-manager

This will create a new folder called expense-manager and install the react dependencies. Navigate into the new folder. It should look like this.

Structure of Root Folder

You can execute the ‘npm start’ command to run this application. It should look like this.

Basic React App

Step 6.1: Install the dependencies

Run the following command to install the dependencies. Do not forget to put ‘ — SAVE’ at the end as it will update the package.json file.

cd expense-manager
npm i axios react-bootstrap react-modal react-router-dom --SAVE
  • axios will be used to send requests to server to fetch or insert data.
  • react-bootstrap lets us use bootstrap components with React.
  • react-modal lets us create a modal dialog in React.
  • react-router-dom lets us use React router.

Step 6.2: Re-arrange the React application.

Let’s re-arrange the react application now. First go to the src directory and create a new folder there called components. Now move App.js from src to components. All our components will reside inside this folder.

Let’s create another folder inside src and call it css. All our css files will reside here. Move App.css from src to css and place the following snippet inside it.

.button-col {
width:100px;
text-align:center;
}
.desc-col {
width:300px;
text-align:left;
}
table {
counter-reset: tableCount;
}
.counterCell:before {
content: counter(tableCount);
counter-increment: tableCount;
}
.counterCell {
text-align: center;
width:50px;
}
.button-center {
text-align: center;
}
.Modal {
position: relative;
top: 250px;
left: 400px;
right: 20px;
bottom: 20px;
background-color: #F5F5F5;
width:500px;
border: 1px solid #000;
border-radius: 4px;
padding: 20px;
}
.Overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rebeccapurple;
}
.button-center {
text-align: center;
}
label{
display:inline-block;
width:200px;
margin-right:30px;
text-align:right;
}
fieldset{
border:none;
margin:0px auto;
}
.closebtn{
float:right;
}

Remove App.test.js, index.css, logo.svg and registerServiceWorker.js from src.

Now inside your src folder, create a new file and call it routes.js.

Step 6.3: Create Components.

We will now create all the components for our frontend. We will have a main component called App.js where all other components will be rendered. We will add a component for each of the four CRUD operations. We will also add tabs to improve presentation so components for monthly and yearly tabs will be added. Note that the code for these components has been taken from my earlier tutorial on MERN stack. To understand how the code works, please refer to this link.

Now head to your components directory and place this code inside App.js.

import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import Add from './Add';
import Update from './Update';
import Delete from './Delete';
import { Tab, Tabs } from 'react-bootstrap';
import YearTabsRouter from './tabs/yearTabsRouter';
import MonthTabs from './tabs/monthTabs';
import styles from '../css/App.css';
export default class App extends React.Component {
constructor() {
super();
this.state = {selectedMonth:'All', selectedYear: 2016, data: [], activeTab:2016};
this.getData = this.getData.bind(this);
}
componentWillReceiveProps(nextProps) {
if(nextProps.history.location.search){
var search = nextProps.history.location.search;
search = search.substring(1);
var searchObj = JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}');
this.setState({activeTab: parseInt(searchObj.year)});
this.setState({selectedYear: searchObj.year});
this.setState({selectedMonth: searchObj.month});
this.getData(this, searchObj.year, searchObj.month);
}else{
this.getData(this, 2016, 'All');
}
}
componentDidMount(){
this.getData(this, 2016, 'All');
}
handleSelect(selectedTab) {
this.setState({
activeTab: selectedTab,
selectedYear: selectedTab
});
}
getData(ev, year, month){
axios.get('http://localhost:8080/expense/'+year+'/'+month)
.then(function(response) {
ev.setState({data: response.data});
ev.setState({selectedYear: parseInt(year)});
ev.setState({selectedMonth: month});
});
}
render() {
return (
<div>
<Tabs activeKey={this.state.activeTab} onSelect={this.handleSelect}>
<Tab eventKey={2016} title={<YearTabsRouter year='2016' />}><MonthTabs year='2016' monthlyActiveTab={this.state.selectedMonth}/></Tab>
<Tab eventKey={2017} title={<YearTabsRouter year='2017' />}><MonthTabs year='2017' monthlyActiveTab={this.state.selectedMonth}/></Tab>
<Tab eventKey={2018} title={<YearTabsRouter year='2018'/>}><MonthTabs year='2018' monthlyActiveTab={this.state.selectedMonth}/></Tab>
<Tab eventKey={2019} title={<YearTabsRouter year='2019'/>}><MonthTabs year='2019' monthlyActiveTab={this.state.selectedMonth}/></Tab>
<Tab eventKey={2020} title={<YearTabsRouter year='2020'/>}><MonthTabs year='2020' monthlyActiveTab={this.state.selectedMonth}/></Tab>
</Tabs>
<Add selectedMonth={this.state.selectedMonth} selectedYear={this.state.selectedYear} />
<table>
<thead>
<tr><th></th><th className='desc-col'>Description</th><th className='button-col'>Amount</th><th className='button-col'>Month</th><th className='button-col'>Year</th><th className='button-col'>Update</th><th className='button-col'>Delete</th></tr>
</thead>
<tbody>
{
this.state.data.map((exp) => {
return <tr><td className='counterCell'></td><td className='desc-col'>{exp.description}</td><td className='button-col'>{exp.amount}</td><td className='button-col'>{exp.month}</td><td className='button-col'>{exp.year}</td><td className='button-col'><Update expense={exp}/></td><td className='button-col'><Delete expense={exp} /></td></tr>
})
}
</tbody>
</table>
</div>
);
}
}

Inside the components directory, create an Add.js file.

import React from 'react';
import {Button} from 'react-bootstrap';
import Modal from 'react-modal';
import axios from 'axios';
import {Link} from 'react-router-dom';
var querystring = require('querystring');
class Add extends React.Component {
constructor() {
super();
this.state = {
description: '',
amount: '',
month: '',
year: '',
messageFromServer: '',
modalIsOpen: false
}
this.handleSelectChange = this.handleSelectChange.bind(this);
this.onClick = this.onClick.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.insertNewExpense = this.insertNewExpense.bind(this);
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal() {
this.setState({
modalIsOpen: true
});
}
closeModal() {
this.setState({
modalIsOpen: false,
description: '',
amount: '',
month: 'Jan',
year: 2016,
messageFromServer: ''
});
}
componentDidMount() {
if(this.props.selectedMonth == 'All'){
this.setState({
month: 'Jan'
});
}else{
this.setState({
month: this.props.selectedMonth
});
}
this.setState({
year: this.props.selectedYear
});
}
componentWillReceiveProps(nextProps){
if(this.props.selectedMonth == 'All'){
this.setState({
month: 'Jan'
});
}else{
this.setState({
month: this.props.selectedMonth
});
}
this.setState({
year:nextProps.selectedYear
})
}
handleSelectChange(e) {
if (e.target.name == 'month') {
this.setState({
month: e.target.value
});
}
if (e.target.name == 'year') {
this.setState({
year: e.target.value
});
}
}
onClick(e) {
this.insertNewExpense(this);
}
insertNewExpense(e) {
var expense = {
description: e.state.description,
amount: e.state.amount,
month: e.state.month,
year: e.state.year
}
axios.post('http://localhost:8080/expense', expense).then(function(response) {
e.setState({
messageFromServer: response.data
});
});
}
handleTextChange(e) {
if (e.target.name == "description") {
this.setState({
description: e.target.value
});
}
if (e.target.name == "amount") {
this.setState({
amount: e.target.value
});
}
}
render() {
if(this.state.messageFromServer == ''){
return (
<div>
<Button bsStyle="success" bsSize="small" onClick={this.openModal}><span className="glyphicon glyphicon-plus"></span></Button>
<Modal
isOpen={this.state.modalIsOpen}
onRequestClose={this.closeModal}
contentLabel="Add Expense"
className="Modal">
<Link to={{pathname: '/', search: '?month='+this.state.month+'&year='+this.state.year }} style={{ textDecoration: 'none' }}>
<Button bsStyle="danger" bsSize="mini" onClick={this.closeModal}><span className="closebtn glyphicon glyphicon-remove"></span></Button>
</Link><br/>
<fieldset>
<label for="description">Description:</label><input type="text" id="description" name="description" value={this.state.description} onChange={this.handleTextChange}></input>
<label for="amount">Amount:</label><input type="number" id="amount" name="amount" value={this.state.amount} onChange={this.handleTextChange}></input>
<label for="month">Month:</label><select id="month" name="month" value={this.state.month} onChange={this.handleSelectChange}>
<option value="Jan" id="Jan">January</option>
<option value="Feb" id="Feb">Febrary</option>
<option value="Mar" id="Mar">March</option>
<option value="Apr" id="Apr">April</option>
<option value="May" id="May">May</option>
<option value="Jun" id="Jun">June</option>
<option value="Jul" id="Jul">July</option>
<option value="Aug" id="Aug">August</option>
<option value="Sep" id="Sep">September</option>
<option value="Oct" id="Oct">October</option>
<option value="Nov" id="Nov">November</option>
<option value="Dec" id="Dec">December</option>
</select>
<label for="year">Year:</label><select id="year" name="year" value={this.state.year} onChange={this.handleSelectChange}>
<option value="2015" id="17">2015</option>
<option value="2016" id="17">2016</option>
<option value="2017" id="17">2017</option>
<option value="2018" id="18">2018</option>
<option value="2019" id="19">2019</option>
<option value="2020" id="20">2020</option>
</select>
</fieldset>
<div className='button-center'>
<br/>
<Button bsStyle="success" bsSize="small" onClick={this.onClick}>Add New Expense</Button>
</div>
</Modal>
</div>
)
}
else{
return (
<div>
<Button bsStyle="success" bsSize="small" onClick={this.openModal}><span className="glyphicon glyphicon-plus"></span></Button>
<Modal
isOpen={this.state.modalIsOpen}
onAfterOpen={this.afterOpenModal}
onRequestClose={this.closeModal}
contentLabel="Add Expense"
className="Modal">
<div className='button-center'>
<h3>{this.state.messageFromServer}</h3>
<Link to={{pathname: '/', search: '?month='+this.state.month+'&year='+this.state.year}} style={{ textDecoration: 'none' }}>
<Button bsStyle="success" bsSize="mini" onClick={this.closeModal}>Close the Dialog</Button>
</Link>
</div>
</Modal>
</div>
)
}
}
}
export default Add;

Now create an Update.js file and place this code inside it.

import React from 'react';
import Modal from 'react-modal';
import axios from 'axios';
import { Button } from 'react-bootstrap';
import { Link } from 'react-router-dom';
var querystring = require('querystring');
class Update extends React.Component {
constructor() {
super();
this.state = {
id: '',
description: '',
amount: '',
month: '',
year: '',
messageFromServer: '',
modalIsOpen: false
}
this.update = this.update.bind(this);
this.handleSelectChange = this.handleSelectChange.bind(this);
this.onClick = this.onClick.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
componentDidMount() {
this.setState({
id: this.props.expense.id,
description: this.props.expense.description,
amount: this.props.expense.amount,
month: this.props.expense.month,
year: this.props.expense.year
});
}
componentWillReceiveProps(nextProps){
this.setState({
id: nextProps.expense.id,
description: nextProps.expense.description,
month:nextProps.expense.month,
year:nextProps.expense.year
})
}
openModal() {
this.setState({
modalIsOpen: true
});
}
closeModal() {
this.setState({
modalIsOpen: false,
messageFromServer: ''
});
}
handleSelectChange(e) {
if (e.target.name == "month") {
this.setState({
month: e.target.value
});
}
if (e.target.name == "year") {
this.setState({
year: e.target.value
});
}
}
handleTextChange(e) {
if (e.target.name == "description") {
this.setState({
description: e.target.value
});
}
if (e.target.name == "amount") {
this.setState({
amount: e.target.value
});
}
}
onClick(e) {
this.update(this);
}
update(e) {
var expense = {
id: e.state.id,
description: e.state.description,
amount: e.state.amount,
month: e.state.month,
year: e.state.year
}

axios.post('http://localhost:8080/expense',expense).then(function(response) {
e.setState({
messageFromServer: response.data
});
});
}
render() {
if(this.state.messageFromServer == ''){
return (
<div>
<Button bsStyle="warning" bsSize="small" onClick={this.openModal}><span className="glyphicon glyphicon-edit"></span></Button>
<Modal
isOpen={this.state.modalIsOpen}
onRequestClose={this.closeModal}
contentLabel="Add Expense"
className="Modal">
<Link to={{pathname: '/', search: '?month='+this.state.month+'&year='+this.state.year }} style={{ textDecoration: 'none' }}>
<Button bsStyle="danger" bsSize="mini" onClick={this.closeModal}><span className="closebtn glyphicon glyphicon-remove"></span></Button>
</Link><br/>
<fieldset>
<label for="description">Description:</label><input type="text" id="description" name="description" value={this.state.description} onChange={this.handleTextChange}></input>
<label for="amount">Amount:</label><input type="number" id="amount" name="amount" value={this.state.amount} onChange={this.handleTextChange}></input>
<label for="month">Month:</label><select id="month" name="month" value={this.state.month} onChange={this.handleSelectChange}>
<option value="Jan" id="Jan">January</option>
<option value="Feb" id="Feb">Febrary</option>
<option value="Mar" id="Mar">March</option>
<option value="Apr" id="Apr">April</option>
<option value="May" id="May">May</option>
<option value="Jun" id="Jun">June</option>
<option value="Jul" id="Jul">July</option>
<option value="Aug" id="Aug">August</option>
<option value="Sep" id="Sep">September</option>
<option value="Oct" id="Oct">October</option>
<option value="Nov" id="Nov">November</option>
<option value="Dec" id="Dec">December</option>
</select>
<label for="year">Year:</label><select id="year" name="year" value={this.state.year} onChange={this.handleSelectChange}>
<option value="2015" id="17">2015</option>
<option value="2016" id="17">2016</option>
<option value="2017" id="17">2017</option>
<option value="2018" id="18">2018</option>
<option value="2019" id="19">2019</option>
<option value="2020" id="20">2020</option>
</select>
</fieldset>
<div className='button-center'>
<br/>
<Button bsStyle="warning" bsSize="small" onClick={this.onClick}>Update</Button>
</div>
</Modal>
</div>
)
}
else{
return (
<div>
<Button bsStyle="warning" bsSize="small" onClick={this.openModal}><span className="glyphicon glyphicon-edit"></span></Button>
<Modal
isOpen={this.state.modalIsOpen}
onAfterOpen={this.afterOpenModal}
onRequestClose={this.closeModal}
contentLabel="Add Expense"
className="Modal">
<div className='button-center'>
<h3>{this.state.messageFromServer}</h3>
<Link to={{pathname: '/', search: '?month='+this.state.month+'&year='+this.state.year}} style={{ textDecoration: 'none' }}>
<Button bsStyle="success" bsSize="mini" onClick={this.closeModal}>Close the Dialog</Button>
</Link>
</div>
</Modal>
</div>
)
}
}
}
export default Update;

Now create a Delete.js file and place this code inside it.

import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import { Button } from 'react-bootstrap';
import { Link } from 'react-router-dom';
class Delete extends React.Component {
constructor(){
super();
this.state={id: '', month: '', year: ''};
this.onClick = this.onClick.bind(this);
this.delete = this.delete.bind(this);
}
componentDidMount() {
this.setState({
id: this.props.expense.id,
month: this.props.expense.month,
year: this.props.expense.year
})
}
componentWillReceiveProps(nextProps){
this.setState({
id: nextProps.expense.id,
month:nextProps.expense.month,
year:nextProps.expense.year
})
}
onClick(e){
this.delete(this);
}
delete(e){
axios.delete('http://localhost:8080/expense',{
params: { id: e.state.id }
})
.then(function(response) {
});
}
render(){
return (
<Button bsStyle="danger" bsSize="small" onClick={this.onClick}>
<Link to={{pathname: '/', search: '?month='+this.state.month+'&year='+this.state.year}} style={{ textDecoration: 'none' }}>
<span className="glyphicon glyphicon-remove"></span>
</Link>
</Button>
)
}
}
export default Delete;

Now let’s add some tabs to improve presentation. Inside the components directory, create a tabs directory. Inside tabs, create monthTabs.js and place the following code inside it.

import React from 'react';
import ReactDOM from 'react-dom';
import { Tab, Tabs } from 'react-bootstrap'
import MonthTabsRouter from './monthTabsRouter'
import YearTabsRouter from './yearTabsRouter'
class MonthTabs extends React.Component {
constructor(){
super();
this.state = {activeTab:''};
this.handleSelect = this.handleSelect.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({activeTab:this.props.year+'-'+nextProps.monthlyActiveTab});
}
handleSelect(selectedTab) {
this.setState({
activeTab: selectedTab
});
}
render(){
return <Tabs activeKey={this.state.activeTab} onSelect={this.handleSelect}>
<Tab eventKey={this.props.year+'-All'} title={<MonthTabsRouter tabId='All' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Jan'} title={<MonthTabsRouter tabId='Jan' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Feb'} title={<MonthTabsRouter tabId='Feb' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Mar'} title={<MonthTabsRouter tabId='Mar' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Apr'} title={<MonthTabsRouter tabId='Apr' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-May'} title={<MonthTabsRouter tabId='May' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Jun'} title={<MonthTabsRouter tabId='Jun' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Jul'} title={<MonthTabsRouter tabId='Jul' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Aug'} title={<MonthTabsRouter tabId='Aug' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Sep'} title={<MonthTabsRouter tabId='Sep' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Oct'} title={<MonthTabsRouter tabId='Oct' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Nov'} title={<MonthTabsRouter tabId='Nov' year={this.props.year}/>}></Tab>
<Tab eventKey={this.props.year+'-Dec'} title={<MonthTabsRouter tabId='Dec' year={this.props.year}/>}></Tab>
</Tabs>
}
}
export default MonthTabs;

Similarly, create monthTabsRouter.js and place the following code inside it.

import React from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router-dom';
class MonthTabsRouter extends React.Component {
constructor(){
super();
this.state={style:{'font-size': '10px'}}
}
render(){
if(this.props.tabId == 'All'){
return <Link to={{pathname: '/', search: '?month=All&year='+this.props.year}} >
<p style={this.state.style}>Show All</p>
</Link>
}
else{
return <Link to={{pathname: '/', search: '?month='+this.props.tabId + '&year='+this.props.year }} >
<p style={this.state.style}>{this.props.tabId} {this.props.year}</p>
</Link>
}
}
}
export default MonthTabsRouter;

Finally, create the yearTabsRouter.js and place the following code inside it.

import React from 'react';
import ReactDOM from 'react-dom';
import { Tab, Tabs } from 'react-bootstrap'
import { Link } from 'react-router-dom';
class YearTabsRouter extends React.Component {
constructor(){
super();
this.state={style:{'font-size': '16px'}}
}
render(){
return <Link to={{pathname: '/', search: '?month=All&year='+this.props.year }} >
<p style={this.state.style}>{this.props.year}</p>
</Link>
}
}
export default YearTabsRouter;

Step 6.4: Finishing Touches.

Lastly, we will add routing. Head to the routes.js file inside the src directory and place the following code there.

import React from 'react';
import { Route, Switch } from 'react-router-dom';
import App from './components/App';
export const Routes = () => (
<Switch>
<Route exact path='/' component={App} />
</Switch>
);
export default Routes;

Now open index.js in the root directory and put the following code inside it.

import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter } from 'react-router-dom'
import Routes from './routes'
ReactDOM.render(
<HashRouter>
<Routes />
</HashRouter>, document.getElementById('root')
);

Finally, head to the index.html file inside the public directory. We will add the bootstrap css to it and change the title.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="./css/App.css"></link>
<title>Expense Manager</title>
</head>
<body>
<h1>Expense Manager</h1>
<div id="root"></div>

</body>
</html>

The structure of our react app directory should look like this.

Structure of React front-end directory

We are all set. Navigate to the root directory from the command prompt and run “npm start”.

You should see this screen.

Expense Manager Frontend

Note that we do not see any expenses here even tho we added expenses in the database during testing. If you right-click and click on “Inspect Element” and select console, you will see an error saying “No ‘Access-Control-Allow-Origin’ header is present on the requested resource”. You can read more about this here.

We will fix this error by telling our backend which cross domain requests are authorized. Open the ExpenseController.java file and add “@CrossOrigin(origins = “http://localhost:3000")” annotation.

Now restart the API and the react app. Head to http://localhost:3000. You should now see the expense objects that you added during testing.

Expense Manager showing the retrieved expenses from the API

Let’s now add a new expense.

Testing the Expense Insertion Feature

Now let’s update the expense we just added.

Testing the Expense Update Feature

Now let’s delete this expense.

Testing the Expense Delete Feature

And that’s a wrap. We have successfully managed to create an Expense Management System with a Spring MVC backend and a React frontend. The code can be found here. I hope this tutorial helps you get started with Spring MVC and React. If you have any questions, please post them in the comments section.

--

--