DEV Community

Cover image for Build a React & Firebase Blog Site: Part 3
Ashlee (she/her)
Ashlee (she/her)

Posted on • Edited on • Originally published at ashleemboyer.com

Build a React & Firebase Blog Site: Part 3

This post was originally published on my personal blog site.

We've reached the third part of our React & Firebase series. In part one, we learned how to set everything up. Last time, we made a page for creating new blog posts. Today, we're going to read single posts from our Firebase Realtime Database and show them in our app.

If you haven't read the first two posts, I have some starter code you can use instead. Just make sure you follow steps 1, 2, & 5 from the first part before you go any further.

Table of Contents

  1. Clone the (Part 3) Starter Code [Optional]
  2. Inspect the Post Component
  3. Connect the Post Component to Firebase

1. Clone the (Part 3) Starter Code [Optional]

Skip this step if you've successfully completed Part 2. Otherwise, you can clone the code by running the following command in the terminal:

git clone https://github.com/ashleemboyer/react-firebase-blog-starter-part-3.git
Enter fullscreen mode Exit fullscreen mode

You'll need to change one file before continuing: src/firebase.js. If you open it, you'll see the following config constant:

const config = {
  apiKey: "<YOUR-API-KEY>",
  authDomain: "<YOUR-AUTH-DOMAIN>",
  databaseURL: "<YOUR-DATABASE-URL>",
  projectId: "<YOUR-PROJECT-ID>",
  storageBucket: "<YOUR-STORAGE-BUCKET>",
  messagingSenderId: "<YOUR-MESSAGE-SENDER-ID>",
  appId: "<YOUR-APP-ID>"
};
Enter fullscreen mode Exit fullscreen mode

The attributes within the constant are used to connect your app to your Firebase project. To find these values, go to your project settings using through the gear icon in the left sidebar of the Firebase console. Scroll down to the "Firebase SDK snippet" under "Your apps" and copy the attributes from what they're calling firebaseConfig. Replace the attributes in your config constant with these values.

Now you can run npm install and then npm run start to see your project in a browser.

2. Inspect the Post Component

You can find this component in the src/pages/post.js file. Let's take a look at what it does right now.

First, it grabs the slug from the URL using the Router we have set up in src/App.js. Components passed in to Route components within a Router have a match prop sent to them. There are other ways this prop is sent to components, and you can read more about that over here.

Next, we have a postSlugs constant which is an array of slugs that exist with a real blog post. If you look at the database, these match the slugs we've given to the first and second blog posts. The problem is that this code isn't dynamic and it's also not connected to the database. We'll come back to this in a few.

Next, we're checking if the slug in the URL bar is one of the postSlugs. If it isn't, then the user is trying to see a post that doesn't actually exist. So, we return a Redirect to our 404 component. You can read more about the Redirect over here.

Finally, we have our return statement. Right now, it returns the same thing for every valid post slug. Instead, we want to show the real blog post content that we have stored in Firebase.

3. Connect the Post Component to Firebase

First, let's add import our getFirebase function so we can try to read from the database. While we're at it, we should also import useState to help manage a couple of things.

import React, { useState } from "react";

import { getFirebase } from "../firebase";
Enter fullscreen mode Exit fullscreen mode

Next, let's think about how what we want to manage with useState. The first thing that comes to mind is a loading state. This will be a boolean that describes whether or not we are trying to load something from the database. We also want a variable for our currentPost that we're trying to read from the database. Replace the postSlugs line with the following two lines of code:

const [loading, setLoading] = useState(true);
const [currentPost, setCurrentPost] = useState();
Enter fullscreen mode Exit fullscreen mode

We want to start in a loading state so the page can show something different to indicate to a user that the page is loading. You can show an animation, GIF, plain text, whatever you please. We'll keep it simple for now and just return some text like this:

if (loading) {
  return <h1>Loading...</h1>;
}
Enter fullscreen mode Exit fullscreen mode

Our database call needs to go right before this if statement, however. If we place it after, it will never be reached because the if statement is making the code return early. Here's what we'll add after our calls to useState and before the if statement we just wrote:

if (loading && !currentPost) {
  getFirebase()
    .database()
    .ref()
    .child(`/posts/${slug}`)
    .once("value")
    .then(snapshot => {
      if (snapshot.val()) {
        setCurrentPost(snapshot.val());
      }
      setLoading(false);
    });
}
Enter fullscreen mode Exit fullscreen mode

Let's update our check on whether a post exists or not. Update postDoesNotExist to the following:

const postDoesNotExist = !currentPost;
Enter fullscreen mode Exit fullscreen mode

These conditions might be a little confusing at first. What are we checking for? It might help to list the steps of execution here, which you can play around with yourself by adding some console logs to the file.

  1. On first load: loading is true and setCurrentPost is undefined, so we go inside of the if statement. Once we make it into the then, if snapshot.val() returns an object (it'll be null if no post exists with the given slug), we call setCurrentPost, making currentPost no longer undefined.
  2. After setCurrentPost call: Making this call will re-render the component. We reach our if (loading && !currentPost) statement again. Since currentPost is now defined, we do not go in the code block again, thus keep ourselves from unnecessarily making calls to the database. We reach the if (loading) statement. loading is still true, so the component returns some text and doesn't do anything else.
  3. After setLoading call: Making this call will re-render the component. loading is now false and currentPost might be undefined or an object. That's where the if (postDoesNotExist) check comes in. If we didn't get a post back from the database, we return a Redirect, like before. Otherwise, we continue on to our final return where we show the post.

I hope these steps aren't overwhelming and they help you see the power of hooks and state management! They're some of the coolest things about React, to me.

Here's how I'm displaying posts:

return (
  <>
    <img src={currentPost.coverImage} alt={currentPost.coverImageAlt} />
    <h1>{currentPost.title}</h1>
    <em>{currentPost.datePretty}</em>
    <p dangerouslySetInnerHTML={{ __html: currentPost.content }}></p>
  </>
);
Enter fullscreen mode Exit fullscreen mode

I also added a 12px margin to the top of <p> elements in src/index.js.

Now, when you click a "Continue reading..." link or manually navigate to one of your posts, you should see something like this:

Screenshot of a single blog post's page.


Did you know I have a newsletter? 📬

If you want to get notified when I publish new blog posts or make major project announcements, head over to https://ashleemboyer.com/newsletter.


Please send me an email or a Twitter DM if you have any questions or concerns. I love hearing from you! 😊

Top comments (8)

Collapse
 
aboutblank profile image
about:blank

Hi Ashlee, great article! Step by step with great comments and explanations.

Just a little fix on the last snippet, it should be:

<>
      <img src={currentPost.coverImage} alt={currentPost.coverImageAlt}></img>
      <h1>{currentPost.title}</h1>
      <em>{currentPost.datePretty}</em>
      <p dangerouslySetInnerHTML={{ __html: currentPost.content }}></p>
    </>

Where img tag closes on the same line and React Fragments has close tag (the last "</>")

Very useful anyways, hope it helps!

Collapse
 
ashleemboyer profile image
Ashlee (she/her) • Edited

Thanks for reading! And good catch! I probably missed the / to close the img and then my auto formatting made the closing fragment tag into a closing img tag. It's fixed now. 😁

Collapse
 
aboutblank profile image
about:blank

Glad to help! Any recommendation on a Firebase authentication/authorization tutorial? Thanks in advance!

Thread Thread
 
ashleemboyer profile image
Ashlee (she/her)

I don't have any off of the top of my head, but I'm planning to add two posts to this series really soon! The next one will have Firebase authentication in it. The Firebase docs are really good in the meantime since you've probably got everything set up after making it this far!

Thread Thread
 
nikrowe profile image
Nik Rowe

Can't wait for the next ones! This was my very 1st intro to Firebase, I have an immense amount to still learn.

Collapse
 
mayorben profile image
mayorben

Unexpected use of history.
That's the error I got

Collapse
 
clebernandes profile image
Cleber Antonio Fernandes

use props.history

Collapse
 
ashleemboyer profile image
Ashlee (she/her)

Hey, mayorben! Do you have a link to your code? I have an idea of what the issue is.