First, head over to nodejs.org and install Node if you already don't have it on your machine. Now, open up the Terminal on Mac or Command Prompt on Windows and run the following command to install create-react-app package.
npm install -g create-react-app
Create Your First React App
Now, type the following command to create a new react app:
create-react-app example2
Now, go to the project folder:
cd example2
Install Dependencies for this Project
npm install @material-ui/core
npm install axios
npm install moment
npm i --save react-render-html
npm install --save react-router-dom
npm install --save sweetalert2
The "package.json" will get update and may look like as below (**Don't update manually**)
Example
{
"name": "example3",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^3.9.2",
"axios": "^0.18.0",
"moment": "^2.24.0",
"react": "^16.8.5",
"react-dom": "^16.8.5",
"react-render-html": "^0.6.0",
"react-router-dom": "^5.0.0",
"react-scripts": "2.1.8",
"sweetalert2": "^8.5.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
Type out the below code into Header.js
Example
import React from 'react';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
export default () => {
return (
<AppBar position="static">
<Toolbar>
<Typography variant="headline" color="colorSecondary" noWrap>
Simple Blog Example
</Typography>
</Toolbar>
</AppBar>
);
};
Create top menu
Example
import React from 'react';
import {Link} from 'react-router-dom';
import './Navigation.css';
const Navigation = () => {
return (
<nav className="col-md-2">
<ul>
<li><div className="sidebar_item"><Link to={'/'}>List All Posts</Link></div></li>
<li><div className="sidebar_item" ><Link to={'/create'}>Add New Post</Link></div></li>
</ul>
</nav>
);
}
export default Navigation;
Example
import Header from './Header';
import Navigation from './Navigation';
export {Header , Navigation}
Top menu style
Example
.sidebar_item {
display: block;
width: 50px;
height: 50px;
margin-top: 20px;
}
nav ul {
position: relative;
display: block;
}
h1{
padding-left: 300px;
text-decoration: none;
color: white;
}
Router to fetch JSON data from API
Example
import React, { Component } from 'react';
import {BrowserRouter, Route, Switch , Redirect} from 'react-router-dom';
import axios from 'axios';
import Swal from 'sweetalert2';
import {Header, Navigation} from './Layout/Layout';
import Posts from './Posts';
import SinglePost from './SinglePost';
import Form from './Form';
import EditPost from './EditPost';
class Router extends Component {
state = {
posts: []
}
componentDidMount() {
this.getPost();
}
getPost = () => {
axios.get(`https://jsonplaceholder.typicode.com/posts`)
.then( res => {
this.setState({
posts: res.data
})
})
}
deletePost = (id) => {
//console.log(id);
axios.delete(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(res => {
if (res.status === 200) {
const posts = [...this.state.posts];
let result = posts.filter(post => (
post.id !== id
));
this.setState({
posts: result
})
}
})
}
createPost = (post) => {
axios.post(`https://jsonplaceholder.typicode.com/posts`, {post})
.then(res => {
if (res.status === 201) {
Swal.fire(
'Post Create',
'It is created correctly.',
'success'
)
let postId = {id: res.data.id};
const newPost = Object.assign({}, res.data.post, postId)
this.setState(prevState => ({
posts: [...prevState.posts, newPost]
}))
}
})
}
editPost = (postUpdate) => {
const {id} = postUpdate;
axios.put(`https://jsonplaceholder.typicode.com/posts/${id}`, {postUpdate})
.then(res => {
if (res.status === 200) {
Swal.fire(
'Post Updated',
'The changes were saved correctly.',
'success'
)
let postId = res.data.id;
const posts = [...this.state.posts];
const postEdit = posts.findIndex(post => postId === post.id)
posts[postEdit] = postUpdate;
this.setState({
posts
})
}
})
}
render() {
return (
<BrowserRouter>
<div className="container">
<Header />
<div className="row justify-content-center">
<Navigation />
<Switch>
<Route exact path="/" render={ () => {
return(
<Posts
posts={this.state.posts}
deletePost={this.deletePost}
/>
);
}} />
<Route exact path="/post/:postId" render={ (props) => {
let idPost = props.location.pathname.replace('/post/', '')
const posts=this.state.posts;
let filter;
filter = posts.filter(post => (
post.id === Number(idPost)
))
return(
<SinglePost
post={filter[0]}
/>
)
}} />
<Route exact path="/create" render={() => {
return(
<Form
createPost={this.createPost}
/>
);
}}
/>
<Route exact path="/edit/:postId" render={ (props) => {
let idPost = props.location.pathname.replace('/edit/', '')
const posts=this.state.posts;
let filter;
filter = posts.filter(post => (
post.id === Number(idPost)
))
return(
<EditPost
post={filter[0]}
editPost={this.editPost}
/>
)
}} />
</Switch>
</div>
</div>
</BrowserRouter>
);
}
}
export default Router;
Design form controls to Add New Post
Example
import React, { Component } from 'react';
class Form extends Component {
//create refs
authorRef = React.createRef();
titleRef = React.createRef();
contentRef = React.createRef();
categoryRef = React.createRef();
createPost = (e) => {
e.preventDefault();
const post = {
author: this.authorRef.current.value,
title: this.titleRef.current.value,
body: this.contentRef.current.value,
category: this.categoryRef.current.value
}
this.props.createPost(post);
}
render() {
return (
<form onSubmit={this.createPost} className="col-md-10">
<legend className="text-center">Create New Post</legend>
<div className="form-group">
<label>Title for the Post:</label>
<input type="text" ref={this.titleRef} className="form-control" placeholder="Title.." />
</div>
<div className="form-group">
<label>Author:</label>
<input type="text" ref={this.authorRef} className="form-control" placeholder="Tag your name.." />
</div>
<div className="form-group">
<label>Content:</label>
<textarea className="form-control" rows="7"cols="25" ref={this.contentRef} placeholder="Here write your content.."></textarea>
</div>
<div className="form-group">
<label>Category</label>
<select ref={this.categoryRef} className="form-control">
<option value="cars">Cars</option>
<option value="nature">Nature</option>
<option value="it">IT</option>
<option value="books">Books</option>
<option value="sport">Sport</option>
</select>
</div>
<button type="submit" className="btn btn-primary">Create</button>
</form>
);
}
}
export default Form;
Load single post page
Example
import React, { Component } from 'react';
import moment from 'moment';
import Divider from '@material-ui/core/Divider';
import Paper from '@material-ui/core/Paper';
import renderHTML from 'react-render-html';
class SinglePost extends Component {
showPost = (props) => {
if (!props.post) return null;
const {title, author, body, category, datestamp} = this.props.post;
return (
<React.Fragment>
<Paper className="single_post">
<h4>Title: {title}</h4>
<Divider light />
<p><b>Autor:</b> {author}</p>
<Divider light />
<p><b>Content:</b> {body}</p>
<Divider light />
<p><b>Category:</b> {category}</p>
<Divider light />
<h5>Create At: {moment(datestamp).format('DD MM YYYY')}</h5>
<div style={{ width: '60%' }}>{renderHTML(body)}</div>
</Paper>
</React.Fragment>
)
}
render() {
return (
<div className=" col-md-10">
{this.showPost(this.props)}
</div>
);
}
}
export default SinglePost;
Generate list of posts for listing page
Example
import React, { Component } from 'react';
import Post from './Post';
import './Post.css';
class Listing extends Component {
showPosts = () => {
const posts = this.props.posts;
if (posts.length === 0) return null;
return (
<div classname="post_list_item"><React.Fragment>
{Object.keys(posts).map(post =>(
<Post
key={post}
info={this.props.posts[post]}
deletePost={this.props.deletePost}
/>
) )}
</React.Fragment></div>
)
}
render() {
return (
<div classname="post_list">
{this.showPosts() }
</div>
);
}
}
export default Listing;
Post listing page
Example
import React, { Component } from 'react';
import Listing from './Listing';
import './Post.css';
class Posts extends Component {
state = { }
render() {
return (
<form className="col-md-10">
<legend className="text-center">Post Listing Page</legend>
<Listing
posts={this.props.posts}
deletePost={this.props.deletePost}
/>
</form>
);
}
}
export default Posts;
Post details in post listing page
Example
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import Swal from 'sweetalert2';
import './Post.css';
import moment from 'moment';
import Paper from '@material-ui/core/Paper';
import Divider from '@material-ui/core/Divider';
class Post extends Component {
confirmDeletion = () => {
const {id} = this.props.info;
Swal.fire({
title: 'Delete this one?',
text: "This action can not be canceled!",
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete',
cancelButtonText: 'No, Cancel'
}).then((result) => {
if (result.value) {
this.props.deletePost(id)
Swal.fire(
'Press OK to back',
'The post has been deleted',
'success'
)
}
})
}
render() {
const {id, title, body, category, datestamp} = this.props.info;
return (
<Paper className="post">
<p className="post_title" cols="10">
<b><span className='post-preview'>
{title.length > 25 ? `${title.substr(0, 25)}...` : title}
</span></b>
</p>
<Divider light />
<p className="post_body">
<span className='post-preview'>
{body.length > 300 ? `${body.substr(0, 300)}...` : body}
</span>
</p>
<Divider light />
<p className="post_category"><b>{category}</b></p>
<Divider light />
<p className="post_datestamp"><b>{moment(datestamp).fromNow()}</b></p>
<div className="post_button">
<ul className="buttons">
<li><Link to={`/post/${id}`} className="btn btn-primary"> Show </Link></li>
<li><Link to={`/edit/${id}`} className="btn btn-warning"> Edit </Link></li>
<li><Link onClick={this.confirmDeletion} className="btn btn-danger">Delete</Link></li>
</ul>
</div>
</Paper>
);
}
}
export default Post;
Post Edit Page
Example
import React, { Component } from 'react';
class EditPost extends Component {
authorRef = React.createRef();
titleRef = React.createRef();
contentRef = React.createRef();
categoryRef = React.createRef();
editPost = (e) => {
e.preventDefault();
const post = {
author: this.authorRef.current.value,
title: this.titleRef.current.value,
body: this.contentRef.current.value,
category: this.categoryRef.current.value,
id: this.props.post.id
}
this.props.editPost(post);
}
loadForm = () => {
if (!this.props.post) return null;
const {title, author, body, category} = this.props.post;
return (
<form onSubmit={this.editPost} className="col-md-10">
<legend className="text-center">Edit Post</legend>
<div className="form-group">
<label>Title for the Post:</label>
<input type="text" ref={this.titleRef} className="form-control" defaultValue={title} />
</div>
<div className="form-group">
<label>Author:</label>
<input type="text" ref={this.authorRef} className="form-control" defaultValue={author} />
</div>
<div className="form-group">
<label>Content:</label>
<textarea className="form-control" rows="7"cols="25" ref={this.contentRef} defaultValue={body}></textarea>
</div>
<div className="form-group">
<label>Category: </label>
<select ref={this.categoryRef} className="form-control" defaultValue={category}>
<option value="cars">Cars</option>
<option value="nature">Nature</option>
<option value="it">IT</option>
<option value="books">Books</option>
<option value="sport">Sport</option>
</select>
</div>
<button type="submit" className="btn btn-primary" >Save changes</button>
</form>
);
}
render() {
return (
<React.Fragment>
{this.loadForm()}
</React.Fragment>
);
}
}
export default EditPost;
Style for Post page
Example
.post{
display: grid;
grid-template-columns: repeat(1, 1fr);
float: left;
margin-right: 10px;
width: 250px;
height: 450px;
margin-top: 50px;
}
ul li{
list-style-type: none;
display: inline;
}
.post_title{
font-size: 1em;
margin-top: 10px;
text-align: center;
}
.post_button{
position: relative;
display: inline-block;
}
.buttons {
position: absolute;
bottom: 0;
}
.post_body{
margin: 10px;
white-space: pre-wrap;
text-align: left;
}
.post_list
{
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.post_category{
text-align: center;
}
.post_datestamp{
text-align: center;
}
You need to replace the "src/App.js" content using the below code to load blog content
Example
import React, { Component } from 'react';
import Router from './components/Router';
class App extends Component {
render() {
return (
<Router />
);
}
}
export default App;
Example
Now launch the application
npm start
This will developed a static website on web server on port 3000 on your machine. This also launch your browser to navigate http://localhost:3000.