In this tutorial, I will show you how to build React.js Material UI examples with a CRUD Application to consume Web API, display and modify data with Router, Axios.
Fullstack:
– React + Spring Boot + MySQL: CRUD example
– React + Spring Boot + PostgreSQL: CRUD example
– React + Spring Boot + MongoDB: CRUD example
– React + Node.js + Express + MySQL: CRUD example
– React + Node.js + Express + PostgreSQL example
– React + Node.js + Express + MongoDB example
– React + Django + Rest Framework example
File upload: Material UI File Upload example with Axios & Progress Bar
Using Bootstrap instead: React.js CRUD example to consume Web API
Serverless:
– React Firebase CRUD with Realtime Database
– React Firestore CRUD App example | Firebase Cloud Firestore
Contents
- Overview of React Material UI examples
- React.js Material UI Component Diagram with Router & Axios
- Technology
- Project Structure
- Setup React Material UI Project
- Import Material UI to React App
- Add React Router to React Material UI App
- Add Common CSS to React Material UI App
- Add AppBar to React Material UI App
- Initialize Axios for React Material UI HTTP Client
- Create Data Service
- Create React Components
- Configure Port for React Material UI Client with Web API
- Run React Material UI examples App
- Conclusion
- Further Reading
- Source Code
Overview of React Material UI examples
We will build a React Tutorial Application using Material UI in that:
- Each Tutorial has id, title, description, published status.
- We can create, retrieve, update, delete Tutorials.
- There is a Search bar for finding Tutorials by title.
Here are screenshots of our React CRUD Application.
– Create an item:
– Retrieve all items:
– Click on Edit button to update an item:
On this Page, you can:
- change status to Published using Publish button
- delete the item using Delete button
- update the item details with Update button
If you want to implement Form Validation, please visit:
React Hook Form & Material UI example
– Search Tutorials by title:
This React Material UI Client consumes the following Web API:
Methods | Urls | Actions |
---|---|---|
POST | /api/tutorials | create new Tutorial |
GET | /api/tutorials | retrieve all Tutorials |
GET | /api/tutorials/:id | retrieve a Tutorial by :id |
PUT | /api/tutorials/:id | update a Tutorial by :id |
DELETE | /api/tutorials/:id | delete a Tutorial by :id |
DELETE | /api/tutorials | delete all Tutorials |
GET | /api/tutorials?title=[keyword] | find all Tutorials which title contains keyword |
You can find step by step to build a Server like this in one of these posts:
– Express, Sequelize & MySQL
– Express, Sequelize & PostgreSQL
– Express, Sequelize & SQL Server
– Express & MongoDb
– Spring Boot & MySQL
– Spring Boot & PostgreSQL
– Spring Boot & MongoDB
– Spring Boot & SQL Server
– Spring Boot & H2
– Spring Boot & Cassandra
– Spring Boot & Oracle
– Python/Django & MySQL
– Python/Django & PostgreSQL
– Python/Django & MongoDB
React.js Material UI Component Diagram with Router & Axios
Now look at the React components that we’re gonna implement:
– The App
component is a container with React Router
. It has AppBar
that links to routes paths.
– TutorialsList
component gets and displays Tutorials.
– Tutorial
component has form for editing Tutorial’s details based on :id
.
– AddTutorial
component has form for submission new Tutorial.
– These Components call TutorialDataService
methods which use axios
to make HTTP requests and receive responses.
Technology
- React 16
- react-router-dom 5.1.2
- axios 0.19.2
- Material UI 4
Project Structure
I’m gonna explain it briefly.
– package.json contains 4 main modules: react
, react-router-dom
, axios
& @material-ui/core
.
– App
is the container that has Router
& AppBar.
– There are 3 components: TutorialsList
, Tutorial
, AddTutorial
.
– http-common.js initializes axios with HTTP base Url and headers.
– css-common.js exports object that contains common styles for using in many components.
– TutorialDataService
has methods for sending HTTP requests to the Apis.
– .env configures port for this React CRUD App.
Setup React Material UI Project
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-material-ui-crud
After the process is done. We create additional folders and files like the following tree:
public
src
components
add-tutorial.component.js
tutorial.component.js
tutorials-list.component.js
services
tutorial.service.js
App.css
App.js
index.js
package.json
Import Material UI to React App
Run command: npm install @material-ui/core
Or: yarn add @material-ui/core
Add React Router to React Material UI App
– Run the command: npm install react-router-dom
.
– Open src/index.js and wrap App
component by BrowserRouter
object.
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
serviceWorker.unregister();
Add Common CSS to React Material UI App
In src folder, add css-common.js file.
This javascript file export an object that includes className
of many components, the key is the className
and nested object is css attribute named in camel case:
export const styles = {
appBar: {
backgroundColor: "#343A40",
height: "50px",
'& .MuiToolbar-regular': {
minHeight: "50px"
}
},
name: {
marginRight: "15px"
},
link: {
textTransform: "unset",
color: "#a5a5a5",
margin: "0 20px",
textDecoration: "unset"
}
From now, everytime we want to use one of the styles above, just import the styles
object, then use props.classes
to access the style.
Don’t forget to call withStyles()
with styles
as input parameter to return a function (higher-order component).
...
import { styles } from "./css-common";
import { ..., withStyles } from '@material-ui/core';
class YourComponent extends Component {
render() {
const { classes } = this.props
return (
<div>
<AppBar className={classes.appBar} position="static">
<Toolbar>
<Typography className={classes.name} variant="h6">
bezKoder
</Typography>
<Link to={"/tutorials"} className={classes.link}>
<Typography variant="body2">
Tutorials
</Typography>
</Link>
</Toolbar>
</AppBar>
...
</div>
);
}
}
export default withStyles(styles)(YourComponent);
Add AppBar to React Material UI App
Open src/App.js, this App
component is the root container for our application, it will contain a AppBar
, and also, a Switch
object with several Route
. Each Route
points to a React Component.
import React, { Component } from "react";
import { Switch, Route, Link } from "react-router-dom";
import "./App.css";
import { styles } from "./css-common"
import AddTutorial from "./components/add-tutorial.component";
import Tutorial from "./components/tutorial.component";
import TutorialsList from "./components/tutorials-list.component";
import { AppBar, Toolbar, Typography, withStyles } from '@material-ui/core';
class App extends Component {
render() {
const { classes } = this.props
return (
<div>
<AppBar className={classes.appBar} position="static">
<Toolbar>
<Typography className={classes.name} variant="h6">
bezKoder
</Typography>
<Link to={"/tutorials"} className={classes.link}>
<Typography variant="body2">
Tutorials
</Typography>
</Link>
<Link to={"/add"} className={classes.link}>
<Typography variant="body2">
Add
</Typography>
</Link>
</Toolbar>
</AppBar>
<Switch>
<Route exact path={["/", "/tutorials"]} component={TutorialsList} />
<Route exact path="/add" component={AddTutorial} />
<Route path="/tutorials/:id" component={Tutorial} />
</Switch>
</div>
);
}
}
export default withStyles(styles)(App);
Initialize Axios for React Material UI HTTP Client
Let’s install axios with command: npm install axios
.
Under src folder, we create http-common.js file with following code:
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080/api",
headers: {
"Content-type": "application/json"
}
});
You can change the baseURL
that depends on REST APIs url that your Server configures.
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Create Data Service
In this step, we’re gonna create a service that uses axios object above to send HTTP requests.
services/tutorial.service.js
import http from "../http-common";
class TutorialDataService {
getAll() {
return http.get("/tutorials");
}
get(id) {
return http.get(`/tutorials/${id}`);
}
create(data) {
return http.post("/tutorials", data);
}
update(id, data) {
return http.put(`/tutorials/${id}`, data);
}
delete(id) {
return http.delete(`/tutorials/${id}`);
}
deleteAll() {
return http.delete(`/tutorials`);
}
findByTitle(title) {
return http.get(`/tutorials?title=${title}`);
}
}
export default new TutorialDataService();
We call axios get
, post
, put
, delete
method corresponding to HTTP Requests: GET, POST, PUT, DELETE to make CRUD Operations.
Create React Components
Now we’re gonna build 3 components corresponding to 3 Routes defined before.
Add item Component
This component has a Form to submit new Tutorial with 2 fields: title
& description
.
components/add-tutorial.component.js
import React, { Component } from "react";
import TutorialDataService from "../services/tutorial.service";
import { TextField, Button, withStyles } from "@material-ui/core"
import { styles } from "../css-common"
class AddTutorial extends Component {
constructor(props) {
super(props);
this.onChangeTitle = this.onChangeTitle.bind(this);
this.onChangeDescription = this.onChangeDescription.bind(this);
this.saveTutorial = this.saveTutorial.bind(this);
this.newTutorial = this.newTutorial.bind(this);
this.state = {
id: null,
title: "",
description: "",
published: false,
submitted: false
};
}
onChangeTitle(e) {
this.setState({
title: e.target.value
});
}
onChangeDescription(e) {
this.setState({
description: e.target.value
});
}
saveTutorial() {
var data = {
title: this.state.title,
description: this.state.description
};
TutorialDataService.create(data)
.then(response => {
this.setState({
id: response.data.id,
title: response.data.title,
description: response.data.description,
published: response.data.published,
submitted: true
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
newTutorial() {
this.setState({
id: null,
title: "",
description: "",
published: false,
submitted: false
});
}
render() {
// ...
}
}
export default withStyles(styles)(AddTutorial)
First, we define the constructor and set initial state, bind this
to the different events.
Because there are 2 fields, so we create 2 functions to track the values of the input and set that state for changes. We also have a function to get value of the form (state) and send the POST request to the Web API. It calls TutorialDataService.create()
method.
For render()
method, we check the submitted
state, if it is true, we show Add button for creating new Tutorial again. Otherwise, a Form will display.
class AddTutorial extends Component {
// ...
render() {
const { classes } = this.props
return (
<React.Fragment>
{this.state.submitted ? (
<div className={classes.form}>
<h4>You submitted successfully!
<Button
size="small"
color="primary"
variant="contained"
onClick={this.newTutorial}>
Add
</Button>
</div>
) : (
<div className={classes.form}>
<div className={classes.textField}>
<TextField
label="Title"
name="title"
value={this.state.title}
onChange={this.onChangeTitle}
required
/>
</div>
<div className={classes.textField}>
<TextField
label="Description"
name="description"
value={this.state.description}
onChange={this.onChangeDescription}
required
/>
</div>
<Button
size="small"
color="primary"
variant="contained"
onClick={this.saveTutorial}>
Submit
</Button>
</div>
)}
</React.Fragment>
);
}
}
List of items Component
This component has:
- a search bar for finding Tutorials by title.
- a tutorials array displayed as a list on the left.
- a selected Tutorial which is shown on the right.
So we will have following state:
searchTitle
tutorials
currentTutorial
andcurrentIndex
We also need to use 3 TutorialDataService
methods:
getAll()
deleteAll()
findByTitle()
components/tutorials-list.component.js
import React, { Component } from "react";
import TutorialDataService from "../services/tutorial.service";
import { Link } from "react-router-dom";
import { styles } from "../css-common"
import { TextField, Button, Grid, ListItem, withStyles } from "@material-ui/core";
class TutorialsList extends Component {
constructor(props) {
super(props);
this.onChangeSearchTitle = this.onChangeSearchTitle.bind(this);
this.retrieveTutorials = this.retrieveTutorials.bind(this);
this.refreshList = this.refreshList.bind(this);
this.setActiveTutorial = this.setActiveTutorial.bind(this);
this.removeAllTutorials = this.removeAllTutorials.bind(this);
this.searchTitle = this.searchTitle.bind(this);
this.state = {
tutorials: [],
currentTutorial: null,
currentIndex: -1,
searchTitle: ""
};
}
componentDidMount() {
this.retrieveTutorials();
}
onChangeSearchTitle(e) {
const searchTitle = e.target.value;
this.setState({
searchTitle: searchTitle
});
}
retrieveTutorials() {
TutorialDataService.getAll()
.then(response => {
this.setState({
tutorials: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
refreshList() {
this.retrieveTutorials();
this.setState({
currentTutorial: null,
currentIndex: -1
});
}
setActiveTutorial(tutorial, index) {
this.setState({
currentTutorial: tutorial,
currentIndex: index
});
}
removeAllTutorials() {
TutorialDataService.deleteAll()
.then(response => {
console.log(response.data);
this.refreshList();
})
.catch(e => {
console.log(e);
});
}
searchTitle() {
TutorialDataService.findByTitle(this.state.searchTitle)
.then(response => {
this.setState({
tutorials: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
render() {
// ...
}
}
export default withStyles(styles)(TutorialsList)
Let’s continue to implement render()
method:
// ...
import { Link } from "react-router-dom";
class TutorialsList extends Component {
// ...
render() {
const { classes } = this.props
const { searchTitle, tutorials, currentTutorial, currentIndex } = this.state;
return (
<div className={classes.form}>
<Grid container>
<Grid className={classes.search} item md={12}>
<TextField
label="Search by title"
value={searchTitle}
onChange={this.onChangeSearchTitle}
/>
<Button
size="small"
variant="outlined"
className={classes.textField}
onClick={this.searchTitle}>
Search
</Button>
</Grid>
<Grid item md={4}>
<h2>Tutorials List
<div className="list-group">
{tutorials &&
tutorials.map((tutorial, index) => (
<ListItem
selected={index === currentIndex}
onClick={() => this.setActiveTutorial(tutorial, index)}
divider
button
key={index}>
{tutorial.title}
</ListItem>
))}
</div>
<Button
className={`${classes.button} ${classes.removeAll}`}
size="small"
color="secondary"
variant="contained"
onClick={this.removeAllTutorials}
>
Remove All
</Button>
</Grid>
<Grid item md={8}>
{currentTutorial ? (
<div className={classes.tutorial}>
<h4>Tutorial
<div className={classes.detail}>
<label>
<strong>Title:
</label>{" "}
{currentTutorial.title}
</div>
<div className={classes.detail}>
<label>
<strong>Description:
</label>{" "}
{currentTutorial.description}
</div>
<div className={classes.detail}>
<label>
<strong>Status:
</label>{" "}
{currentTutorial.published ? "Published" : "Pending"}
</div>
<Link
to={"/tutorials/" + currentTutorial.id}
className={classes.edit}
>
Edit
</Link>
</div>
) : (
<div>
<br />
<p className={classes.tutorial}>Please click on a Tutorial...
</div>
)}
</Grid>
</Grid>
</div>
);
}
}
export default withStyles(styles)(TutorialsList)
If you click on Edit button of any Tutorial, the app will direct you to Tutorial page.
We use React Router Link
for accessing that page with url: /tutorials/:id
.
You can add Pagination to this Component, just follow instruction in the post:
React Pagination with API using Material-UI
Item details Component
We’re gonna use the component lifecycle method: componentDidMount()
to fetch the data from the Web API.
For getting data & update, delete the Tutorial, this component will use 3 TutorialDataService
methods:
get()
update()
delete()
components/tutorial.component.js
import React, { Component } from "react";
import TutorialDataService from "../services/tutorial.service";
import { styles } from "../css-common"
import { TextField, Button, withStyles } from "@material-ui/core";
class Tutorial extends Component {
constructor(props) {
super(props);
this.onChangeTitle = this.onChangeTitle.bind(this);
this.onChangeDescription = this.onChangeDescription.bind(this);
this.getTutorial = this.getTutorial.bind(this);
this.updatePublished = this.updatePublished.bind(this);
this.updateTutorial = this.updateTutorial.bind(this);
this.deleteTutorial = this.deleteTutorial.bind(this);
this.state = {
currentTutorial: {
id: null,
title: "",
description: "",
published: false
},
message: ""
};
}
componentDidMount() {
this.getTutorial(this.props.match.params.id);
}
onChangeTitle(e) {
const title = e.target.value;
this.setState(function (prevState) {
return {
currentTutorial: {
...prevState.currentTutorial,
title: title
}
};
});
}
onChangeDescription(e) {
const description = e.target.value;
this.setState(prevState => ({
currentTutorial: {
...prevState.currentTutorial,
description: description
}
}));
}
getTutorial(id) {
TutorialDataService.get(id)
.then(response => {
this.setState({
currentTutorial: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
updatePublished(status) {
var data = {
id: this.state.currentTutorial.id,
title: this.state.currentTutorial.title,
description: this.state.currentTutorial.description,
published: status
};
TutorialDataService.update(this.state.currentTutorial.id, data)
.then(response => {
this.setState(prevState => ({
currentTutorial: {
...prevState.currentTutorial,
published: status
}
}));
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
updateTutorial() {
TutorialDataService.update(
this.state.currentTutorial.id,
this.state.currentTutorial
)
.then(response => {
console.log(response.data);
this.setState({
message: "The tutorial was updated successfully!"
});
})
.catch(e => {
console.log(e);
});
}
deleteTutorial() {
TutorialDataService.delete(this.state.currentTutorial.id)
.then(response => {
console.log(response.data);
this.props.history.push('/tutorials')
})
.catch(e => {
console.log(e);
});
}
render() {
// ...
}
}
export default withStyles(styles)(Tutorial)
Configure Port for React Material UI Client with Web API
Because most of HTTP Server use CORS configuration that accepts resource sharing retrictted to some sites or ports, so we also need to configure port for our App.
In project folder, create .env file with following content:
PORT=8081
Now we’ve set our app running at port 8081
.
Run React Material UI examples App
You can run our App with command: npm start
.
If the process is successful, open Browser with Url: http://localhost:8081/
and check it.
This React Client will work well with following back-end Rest APIs:
– Express, Sequelize & MySQL
– Express, Sequelize & PostgreSQL
– Express, Sequelize & SQL Server
– Express & MongoDb
– Spring Boot & MySQL
– Spring Boot & PostgreSQL
– Spring Boot & MongoDB
– Spring Boot & SQL Server
– Spring Boot & H2
– Spring Boot & Cassandra
– Spring Boot & Oracle
– Python/Django & MySQL
– Python/Django & PostgreSQL
– Python/Django & MongoDB
Conclusion
Today we’ve built a React CRUD Application successfully using Material UI with React Router & Axios. Now we can consume REST APIs, display, search and modify data in a clean way. I hope you apply it in your project at ease.
You can add Pagination Component with this tutorial:
React Pagination with API using Material-UI
Happy learning, see you again!
Further Reading
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Serverless with Firebase:
– React Firebase CRUD with Realtime Database
– React Firestore CRUD App example | Firebase Cloud Firestore
Integration:
– Integrate React with Spring Boot
– Integrate React with Node.js Express
Material UI File upload:
Material UI File Upload example with Axios & Progress Bar
Source Code
You can find the complete source code for this tutorial on Github.
If you want to implement Form Validation, please visit:
React Hook Form & Material UI example
Thank you!
good MUI React tutorial, absolutely nothing else we want 😀