Advertisement
  1. Code
  2. JavaScript
  3. React

Creating a Blogging App Using React, Part 3: Add & Display Posts

Scroll to top
This post is part of a series called Creating a Blogging App Using React.
Creating a Blogging App Using React, Part 2: User Sign-Up
Creating a Blogging App Using React, Part 4: Update & Delete Posts

In the previous parts of this tutorial series, you saw how to implement the sign-up and sign-in functionality. In this part of the tutorial, you'll implement the user home page and allow the adding and displaying of blog posts.

I've created a simple UX layout to help you visualise the landing page of the blog. This will help you follow the post better. The functionalities to be covered are:

  1. how to pass data between two pages via the route
  2. how to save a post
  3. how to open the chosen post
simple UX layoutsimple UX layoutsimple UX layout

Getting Started

Let's get started by cloning the source code from this repository. Once the repository is cloned, you will be able to see the client-side code. Navigate to the project directory, and run the following command to install all the essential dependencies.

1
npm install
2
npm start

The Server

So far in the tutorial, we have been able to register and check if a user is present with the system. Now, we will see how to add and retrieve posts. To do this, we need to add a few more endpoints in the express server. 

To get all posts from the database, we'll use the following get call.

1
app.get('/api/get/allposts', (req, res, next ) => {
2
  pool.query(`SELECT * FROM posts`, 
3
            (q_err, q_res) => {
4
                  res.json(q_res.rows)
5
  })
6
})

To get a specific post from the database, the query should contain the required post id. With this information, the following get endpoint can be used to retrieve a specific post. 

1
app.get('/api/get/post', (req, res, next) => {
2
  const post_id = req.query.post_id
3
4
  pool.query(`SELECT * FROM posts

5
              WHERE pid=$1`,
6
            [ post_id ], (q_err, q_res) => {
7
                res.json(q_res.rows)
8
      })
9
} )

When you want to insert a new post into the database, you can use the following endpoint with a post call.

1
app.post('/api/post/posttodb', (req, res, next) => {
2
  const values = [ req.body.title, 
3
                   req.body.body,
4
                   req.body.uid, 
5
                   req.body.username]
6
  pool.query(`INSERT INTO posts(title, body, user_id, author, date_created)

7
              VALUES($1, $2, $3, $4, NOW() )`,
8
           values, (q_err, q_res) => {
9
          if(q_err) return next(q_err);
10
          res.json(q_res.rows)
11
    })
12
})

The above changes are adequate to prepare the client for displaying and adding new posts to the database. The previous posts talk about how to set up the server and get started with the project.

The Client

A little recap: so far in the series, the client application has a sign-in and sign-up page. In this tutorial, we are going to build the landing page and a page for displaying the post. 

We will modify our initial routes array to include a new route for /post and another for /landing.

1
const router = createBrowserRouter([
2
  {
3
    path: "/",
4
    element: <App/>,
5
  },
6
  .
7
  .
8
  {
9
    path: "/landing",
10
    element: <Landing />,
11
  },
12
  {
13
    path: "/post",
14
    element: <Post />,
15
  }
16
]);

The Landing Page

Once the user signs in or signs up, they will be redirected to the landing page. /landing/index.js will have the code discussed below.

During redirection, the email, UID, and user name will be sent to the landing page. On the landing page, we will extract this information from the location.

1
import { useLocation } from 'react-router-dom';
2
.
3
.
4
const { state } = useLocation();
5
const { email, username, uid } = state;

We are going to define the following state variables:

  • showModal: decide if the modal has to be shown or not.
  • posts: array of posts to be shown to the user on the landing page. By default, it will be an empty array.
  • refresh: reload the page, if true.
1
const [showModal, setShowModal] = useState(false)
2
const [refresh, setRefresh] = useState(false)
3
const [posts, setPosts] = useState([])
4
  

When the landing page is loaded, the useEffect hook will be called. The [] in the definition of useEffect specifies that useEffect will be called only once. Inside this useEffect, we will load all the user's posts.

1
useEffect(()=>{
2
    loadAllPostsOfUser()
3
},[])

The loadAllPostsOfUser will use the /api/get/allposts endpoint to fetch all the user's posts. Once the response arrives, we will populate the state posts. An interesting thing about React functional hooks is that changes will reflect automatically. When the state of the post changes, the UI will render again.

1
const loadAllPostsOfUser = () => {
2
    axios.get('/api/get/allposts')
3
      .then(res => setPosts(res.data) )
4
      .catch((err) => console.log(err))
5
}

Now that we have the posts, let's build the UI as seen in the mock UX. For this, we will iterate through the posts and display the post title. On clicking the post title, we will navigate to a new route called /post. During navigation, we'll send details of the post_id to /post to load the right post.

1
const openPost = (post_id) => {
2
    navigate(`/post`, {
3
      email: email,
4
      uid: uid,
5
      username: username,
6
      post_id: post_id
7
    })
8
  }
9
.
10
.
11
return (
12
<div className="post_container">
13
    {posts.map((post, index) => 
14
    <React.Fragment>
15
      <span>{index + 1}</span>

16
      <span className="post_title" onClick={()=>openPost(post.id)}>{post.name}</span>

17
    </React.Fragment>

18
    )}
19
</div>

20
)

Now, let's work on the header of the landing page. The header has the following items:

  • a button to open a modal for creating a new post
  • a button to sign out of the application
  • a title that welcomes the user, along with their email ID. The username and email are fetched from the location object.
1
const openAddNewPostModal = () => {
2
    setShowModal(true)
3
}
4
.
5
.
6
.
7
<div className='title'>
8
    Hello {username} [{email}]!
9
    <button onClick={()=>openAddNewPostModal()}>Add New Post</button>

10
    <button onClick={()=>signOutOfSystem()}>Sign out</button>

11
</div>

Next, we are going to create modal.js and modal.css. The modal will be seen when the user clicks on the Add New Post button. The modal appears below.

ModalModalModal

The modal takes the following properties:

  • handleClose: a function that will be called when the user closes the modal.
  • show: a boolean parameter that decides if the modal should be shown or hidden
  • children: a Node that has the elements to be shown inside the modal
1
const Modal = ({ handleClose, show, children }) => {
2
    ...
3
};

The modal strongly depends on its CSS. We will import the modal's CSS from modal.css. If the modal is shown, the parameter show will be true. With show equal to true, the classname display-block with the property display: block will be applied to the body of the modal. This will make the children visible in the modal. And when show is false, display-none with the property display:none will be applied. This will hide the children inside the modal. 

1
import './modal.css';
2
3
const Modal = ({ handleClose, show, children }) => {
4
  const showHideClassName = show ? "modal display-block" : "modal display-none";
5
    ....
6
};

Now, showing children elements inside the modal is simple. The modal has a button that can be used to close and hide it. Modal.css has the styling required to change the look and feel of the modal. And {children} will be passed from landing.js

1
import './modal.css';
2
3
const Modal = ({ handleClose, show, children }) => {
4
  const showHideClassName = show ? "modal display-block" : "modal display-none";
5
6
  return (
7
    <div className={showHideClassName}>
8
      <section className="modal-main">
9
        {children}
10
        <button className="buttonPosition" type="button" onClick={handleClose}>
11
          Close
12
        </button>

13
      </section>

14
    </div>

15
  );
16
};

Modal.css will have the following code.

1
.modal {
2
  position: fixed;
3
  top: 0;
4
  left: 0;
5
  width:100%;
6
  height: 100%;
7
  background: rgba(0, 0, 0, 0.6);
8
}
9
  
10
.modal-main {
11
  position:fixed;
12
  background: white;
13
  width: 80%;
14
  height: 500px;
15
  border-radius: 20px;
16
  top:50%;
17
  left:50%;
18
  transform: translate(-50%,-50%);
19
  display: flex;
20
  flex-direction: column;
21
  padding: 50px;
22
}
23
24
.display-block {
25
  display: block;
26
}
27
28
.display-none {
29
  display: none;
30
}

Our next goal is to call modal.js from landing.js. For this, we will use the showModal state variable to decide if the modal needs to be visible or not. handleClose is a function passed from landing.js to modal.js. handleClose will set the value of showModal. The handleSubmit function will call the endpoint /api/post/posttodb with details of the post and the user. This endpoint will add a new post in the database.

1
...
2
import './index.css';
3
import Modal from './modal'
4
...
5
6
function Landing() {
7
 ...
8
  const { email, username, uid } = state;
9
  const [showModal, setShowModal] = useState(false)
10
 ...
11
12
  useEffect(()=>{
13
    setShowModal(false)
14
  }, [refresh])
15
16
 ...
17
18
  const openAddNewPostModal = () => {
19
    setShowModal(true)
20
  }
21
22
  const handleSubmit = (event) => {
23
    event.preventDefault()
24
    const data = {title: event.target.title.value,
25
                  body: event.target.body.value,
26
                  username: username,
27
                  uid: uid}
28
29
    axios.post('/api/post/posttodb', data)
30
      .then(response => console.log(response))
31
      .catch((err) => console.log(err))
32
      .then(setTimeout(() => setRefresh(!refresh), 700) )
33
  }
34
35
  return (
36
    <div className="container">
37
     ...
38
      <Modal show={showModal} handleClose={()=>setShowModal(!showModal)}>
39
          <form onSubmit={handleSubmit} className='modalContainer'>
40
            <span>Title of the Blog</span><input type='text'/>
41
            <span>Subject</span><textarea/>
42
            <button type='submit'>Submit</button>
43
            </form>
44
      </Modal>
45
      ...
46
    </div>
47
  );
48
}
49
50
export default Landing;

Once the post is successfully added to the database, the value of refresh will be toggled. Any change in the refresh value will call the useEffect shown below. This will set false to showModal and hide the modal. Also, the whole landing.js page will be reloaded. Consequently, the newly added post will be seen in the list of posts.  

1
  useEffect(()=>{
2
    setShowModal(false)
3
  }, [refresh])

The last step in adding a post is to show it in the UI. For this, we will create two files: post.js and post.css. The post.js page will be loaded when the user clicks on the image name in landing.js. Our post.js page appears as below. 

post.js pagepost.js pagepost.js page

Upon loading post.js, the post_id, usename, email, and uid will be available through the useLocation hook. With the post_id, we will call the /api/get/post endpoint to fetch the post. 

1
  useEffect(()=>{
2
    if(post_id && uid) {
3
      axios.get('/api/get/post',
4
        {params: {post_id: post_id}} )
5
        .then(res => res.data.length !== 0
6
                ?   setPost({likes: res.data[0].likes,
7
                        like_user_ids: res.data[0].like_user_id,
8
                        post_title: res.data[0].title,
9
                        post_body: res.data[0].body,
10
                        post_author: res.data[0].author
11
                      })
12
                 : null
13
              )
14
        .catch((err) => console.log(err) )
15
    }
16
  }, [post_id])

Once the post is retrieved from the database, the state variable post will be populated with its details. As soon as the state variable is modified, the UI will render again with the post. We will use the optional chaining operator (?) to make sure the UI does not break when the state variable post is empty.

1
import React, { useEffect, useState } from 'react';
2
import { useLocation } from 'react-router-dom';
3
import './post.css';
4
import './index.css';
5
import axios from 'axios';
6
7
function Post() {
8
  const { state } = useLocation();
9
  const { email, username, uid, post_id } = state || {username: 'Tuts+ Envato', email: 'tuts@envato.com', uid: '123', post_id: 1}
10
  const [post, setPost] = useState()
11
  
12
  useEffect(()=>{
13
    if(post_id && uid) {
14
      axios.get('/api/get/post',
15
        {params: {post_id: post_id}} )
16
        .then(res => res.data.length !== 0
17
                ?   setPost({likes: res.data[0].likes,
18
                        like_user_ids: res.data[0].like_user_id,
19
                        post_title: res.data[0].title,
20
                        post_body: res.data[0].body,
21
                        post_author: res.data[0].author
22
                      })
23
                 : null
24
              )
25
        .catch((err) => console.log(err) )
26
    }
27
  }, [post_id])
28
29
  const signOutOfSystem = () => {
30
  ...
31
  }
32
  
33
  return (
34
    <div className="container">
35
       <div className='title'>
36
      Hello {username} [{email}]!
37
38
      {!uid && <div>Seems like you are not logged in!</div>}
39
      <button onClick={()=>signOutOfSystem()}>Sign out</button>
40
      </div>
41
      <br/>
42
      <div>
43
        {post?.post_title} by {post.post_author}
44
      </div>
45
      <br/>
46
      <br/>
47
      <section>
48
        {post?.post_body}
49
      </section>
50
      <br/>
51
      <br/>
52
      <aside>
53
        {post.likes > 0 ? <div>Liked by {post?.likes} readers</div>: null} 
54
      </aside>
55
    </div>
56
  );
57
}
58
59
export default Post;

Conclusion

With this, we have built landing.js, modal.js, and post.js to add and show posts in our blog. Also, we have designed and developed the get post, add post, and show all posts endpoints in our Express server.

The entire workflow is extremely interesting. It has many components that come together quite easily with the help of functional React hooks. We have taken a closer look at a few commonly used hooks like useLocation, useNavigation, useState, and useEffect. As you start building this blog by yourself, you will see how often these hooks are used in React. 

The next part of this tutorial focuses on how to edit and delete a post. Try to implement this functionality yourself—before reading the next post! 

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.