Random Quote – Part 7 – React and JSON: Using various front end stacks

7 min read

Building a random quote machine with React and JSON

This is Part 7 of the Making a random quote machine in a few different flavors series.

You can read more about this project’s background on the project’s page. Or start from the beginning with part 1 of this series.

In flavor #6, the app’s UI is built using a User Interface Library (React) and I have the data (quotes) inside an array. In this flavor, I’m still using React but the app calls an asynchronous endpoint to request the data.


In Part 8 (to be posted next week), I will cover an eighth flavor and will be using React Hooks to build the app’s UI.

If you’re curious about the next flavors and would like to make a writer very happy today, you should join Morse Wall’s newsletter.


Flavor #7: HTML + CSS + React + JSON with quotes

Like previously in flavor #5, I’m storing the quotes in a separate JSON file in jsDelivr, a public CDN.

There is a limited number of quotes from the JSON in the code snippet below (so I can illustrate the point), but many more quotes in the JSON in production.

{
  "quotes": [
    {
      "quoteText": "\"Many of you appear concerned that we are wasting valuable lesson time, but I assure you we will go back to school the moment you start listening to science and give us a future.\"",
      "quoteAuthor": "@GretaThunberg"
    },
    {
      "quoteText": "\"I was fortunate to be born in a time and place where everyone told us to dream big. I could become whatever I wanted to. I could live wherever I wanted to. People like me had everything we needed and more. Things our grandparents could not even dream of. We had everything we could ever wish for and yet now we may have nothing. Now we probably don’t even have a future any more.\"",
      "quoteAuthor": "@GretaThunberg"
    },
    {
      "quoteText": "\"That future was sold so that a small number of people could make unimaginable amounts of money. It was stolen from us every time you said that the sky was the limit, and that you only live once. You lied to us. You gave us false hope. You told us that the future was something to look forward to.\"",
      "quoteAuthor": "@GretaThunberg"
    }
  ]
}

Controlling asynchronous code in React

In this flavor, I need to fetch data from a JSON file. I’m then populating an array (quotes) with the data fetched.

//script.js
"use strict";

//defining variable for array to be populated with JSON content
let quotes;

Like in flavor #6, I’m then selecting a random quote from the array and setting the quote in the component’s internal state.

While doing the above, I also need to make sure that I only try to set the randomly selected quote in the component’s internal state after I’ve finalized fetching the data from the JSON file. Otherwise, I’d eventually get quotes is undefined on my browser console. Sometimes, things must happen in order.

In other words: Firstly, I need to define an asynchronous function (to fetch the data) that when executed won’t pause the execution of the rest of the code. Secondly, I need to adjust the component’s constructor to make sure the app doesn’t try to access the data in the quotes array before the array has been populated.

Adjusting the constructor, I’m declaring the state property, but not trying to set it up with data (since I haven’t fetched it yet).

// script.js
"use strict";

//defining variable for array to be populated with JSON content
let quotes;
//defining the stateful component. This creates the JavaScript class App that extends the React.Component class. With this, App can now access React features.
class App extends React.Component {
  constructor(props) {
    super(props);
    //binding the class methods defined further below. This way the class methods can use this to access properties on the class (such as state) inside the scope of the method.
    this.random = this.random.bind(this);
    this.randomQuoteFunction = this.randomQuoteFunction.bind(this);
    this.chosenRandomQuoteToState = this.chosenRandomQuoteToState.bind(this);
    //defining the component initial state. Initializing state.
    this.state = {
      quoteTextChosen: "",
      quoteAuthorChosen: ""
    };
  }
}

Data fetching using the componentDidMount() lifecycle method

To make sure things happen in order, I’m using React’s componentDidMount() lifecycle method that allows me to catch a component at a certain time of its lifecycle.

Every component in React goes through a lifecycle of events. Components go through a cycle similar to that of birth, growth, and death.

Here, I am catching the component when it has been mounted and ready, i.e. componentDidMount() is called as soon as the component (App) is mounted and ready.

Disclaimer: React Hooks and React Suspense

I should also mention that React is changing fast and and the method exposed here could be already obsolete by the time you’ll read this article. At the time of writing, React Hooks with useEffect is available on a stable release and can be used to ship this application under a slightly different structure.

Hooks are, however, still very new and “best practices” are still being figured out for some of the less common patterns. Furthermore, a new way to fetch data is in the works with React Suspense.

All in all, the recommendation here is: Don’t go rewriting all your components unless you don’t mind being an early adopter.

It is also a best practice to place API calls or any request for resources from the network in the componentDidMount() lifecycle method. So, that is where I’m fetching the data from the JSON file.

//script.js
"use strict";

//defining variable for array to be populated with JSON content
let quotes;
//defining the stateful component. This creates the JavaScript class App that extends the React.Component class. With this, App can now access React features.
class App extends React.Component {
  constructor(props) {
    super(props);
    //binding the class methods defined further below. This way the class methods can use this to access properties on the class (such as state) inside the scope of the method.
    this.random = this.random.bind(this);
    this.randomQuoteFunction = this.randomQuoteFunction.bind(this);
    this.chosenRandomQuoteToState = this.chosenRandomQuoteToState.bind(this);
    //defining the component initial state. Initializing state.
    this.state = {
      quoteTextChosen: "",
      quoteAuthorChosen: ""
    };
  }

  //asynchronous function that fetches JSON content, populates the quotes array and updates state with a quote (following the fetch being complete)
  async componentDidMount() {
    const responseJSON = await fetch(
      "https://cdn.jsdelivr.net/gh/morsewall/jsondb@master/db.json"
    );
    const responseObject = await responseJSON.json();
    quotes = responseObject.quotes;
  }
}

Any calls to setState() within componentDidMount() will trigger a re-rendering of the component. When I fetch data from the JSON file and set the component state with the data returned, a re-rendering will be automatically triggered once the app received the data. This is great and it’s exactly what I need since I need to ship the “I want to be welcomed by a quote when I first load the app” user story. So, here it goes, updating state:

"use strict";

//defining variable to be populated with JSON content
let quotes;

//defining the stateful component. This creates the JavaScript class App that extends the React.Component class. With this, App can now access React features.
class App extends React.Component {
  constructor(props) {
    super(props);
    //binding the class methods defined further below. This way the class methods can use this to access properties on the class (such as state) inside the scope of the method.
    this.random = this.random.bind(this);
    this.randomQuoteFunction = this.randomQuoteFunction.bind(this);
    this.chosenRandomQuoteToState = this.chosenRandomQuoteToState.bind(this);
    //defining the component initial state. Initializing state.
    this.state = {
      quoteTextChosen: "",
      quoteAuthorChosen: ""
    };
  }

  //asynchronous function that fetches JSON content, populates the quotes array and updates state with a quote (following the fetch being complete)
  async componentDidMount() {
    const responseJSON = await fetch(
      "https://cdn.jsdelivr.net/gh/morsewall/jsondb@master/db.json"
    );
    const responseObject = await responseJSON.json();
    quotes = responseObject.quotes;
    let quote = this.randomQuoteFunction(quotes);
    this.setState({
      quoteTextChosen: quote.quoteText,
      quoteAuthorChosen: quote.quoteAuthor
    });
  }

  random(array) {
    return Math.floor(Math.random() * array.length);
  }

  randomQuoteFunction(array) {
    return array[this.random(array)];
  }
}

Done. The rest of the code remains the same as in flavor #6 and you can check the project live. Source code for flavor #7 in Github.

Live project Source code


In Part 8 (to be posted next week), I will cover an eighth flavor and will be using React Hooks to build the app’s UI.

If you’re curious about the next flavors, you should join Morse Wall’s newsletter. I think you’ll like it a lot!


So, what did you like about this post? Leave a comment or come say hello on Twitter!

Also, if you have built a random quote machine after reading this post, share it below as a comment. Really excited to see what you create!

Happy coding!

Enjoyed this content?

Help keep it coming by sending a donation or buying me coffees.
You can also join Morse Wall's newsletter, or subscribe to various site feeds to get notified of new posts or follow Morse Wall on social media.

Leave a comment

by Pat
by Pat