Jump to content

Functional UI Development with ReactJS

From Wikiversity

Introduction

[edit | edit source]

What is ReactJS?

[edit | edit source]

React is a front-end JavaScript library created by Facebook that is used to create intuitive UI experiences. Put more simply, it allows web programmers to quickly create the parts of a website that users can see and interact with (the front-end).[1] There are many advantages to using the React library over plain HTML, CSS, and JavaScript including code reusability and efficiency. In React, the UI is broken into many smaller pieces, called components. Each component should be designed to exist independently of the others and is responsible for handling changes to the data it displays.[2] For example, one component on a video sharing website might be responsible for displaying various meta-data pertaining to a video (date created, owner, number of likes, etc.). In React, this single component can be used over and over again to display information about all the videos on a page by simply passing different meta-data for each video to a separate component. Now, if the number of likes on a video changes over time, only the component that renders the meta-data for that particular video needs to update as opposed to the entire page. For a more detailed description of ReactJS and its advantages, check out the official React documentation here.

Prerequisites

[edit | edit source]

Although React itself is not terribly complex, it can be considered a more advanced topic in web development since it requires a fair bit of prerequisite knowledge. Before starting this tutorial, you'll want to be familiar with the basics of the following:

  • HTML - Know how to create a webpage with plain HTML tags. You don't need to know every type of tag, but be familiar with the main ones (div, span, p, h, a, li, input, button, etc.)
  • CSS - Understand how to use stylesheets to modify the layout and aesthetics of a webpage.
  • JavaScript (especially ES6) - Since React is a JavaScript library, having a strong background in JavaScript is important. Many key features introduced in ECMAScript6 (ES6), a release of JavaScript will be helpful in writing clear and simple React code.
  • Terminal/command line basics - You should be familiar with the fundamental commands to navigate a file system using the terminal. If you are using a windows computer, you will either be using the command prompt, or you can download git bash to use a linux-style terminal that connects to git. Make sure you are comfortable using whichever one you choose.

Getting Started

[edit | edit source]

The simplest way to begin using react is to include it from a Content Delivery Network (CDN) inside a script tag in an HTML file.[3] However, this method is also quite limiting and makes it difficult to scale the application to many components and pull in third-party tools. So, in this guide we will be starting a React application from scratch using create-react-app, a tool provided by Facebook to rapidly create a new single page react application.

Downloading Node.js

[edit | edit source]

In order to run our React app in development, we need a server. Node.js is a server-side JavaScript environment that is becoming increasingly popular due to its simplicity, asynchronous nature, and familiarity (we can use JavaScript for the front and back end). We will be running our React app on a local Node server for development. To download Node, head over to their website here and follow the download/installation instructions for your operating system and architecture (32- or 64-bit). At some point in the installation, you will have the option to select which features you want to install. Node package manager (npm) should automatically be selected here, but make sure to add it if not as we will need it to start our React app. After installation, you can verify that node and npm have installed successfully by opening a terminal (or command prompt on windows) and typing node -v and npm -v. If both commands show a version number such as v10.15.3 and 6.14.6 then you are good to go. (Note that your versions may be different and that is fine)

Creating a React Application

[edit | edit source]

Navigate to the folder or location on your file system where you want to create the application, open this location in a terminal or command prompt and run the following command:

npx create-react-app app-name

Replace the italicized "app-name" with whatever name you want your React application to be called. This command may take a few minutes to run as it is doing a lot of work behind the scenes to set up a React environment and install the numerous required dependencies/packages. If you are interested in learning more about the setup, this article provides a more detailed description.

Once the command finishes, a folder will be created in the working directory with whatever name you provided to create-react-app. To run the app, type cd app-name and then npm start. This will launch a Node server on port 3000 and run a template React webpage provided by create-react-app in your default browser.

Understanding the Structure of a React App

[edit | edit source]

index.html

[edit | edit source]

After creating a new React application, a bunch of different files and folders can be found inside your root application folder (the folder with your app name as the title). The two most important folders for us right now are /public and /src. Inside the /public folder, you will find an index.html file.[4] Go ahead and open that file now in your preferred text editor. What you will see is a pretty standard HTML file containing some link tags to pull in some icons, meta tags, and a body tag. Inside the body tag, there is a single div tag:

<div id="root"></div>

As we know from working with HTML, div tags are simply containers for other HTML elements to help us organize content better. This div tag with the id "root" is where our entire React application is going to live. All the components and HTML elements we create will eventually be appended into this container to render a complete webpage. If we look inside the /src folder, we can see where this is happening.

index.js

[edit | edit source]

Open the file called /src/index.js in a text editor. Note the file extension of .js instead of .html. From now on, we will be working solely with JavaScript files (.js) to create React components and using JSX to render HTML elements. If you are not familiar with JSX, it stands for JavaScript XML and is the preferred method of adding HTML to React JavaScript files. It is an extension of JavaScript and provides a simpler alternative to writing numerous createElement() and appendChild() type function calls to insert HTML into the DOM. I'll be explaining it's syntax with examples as we go along, but I would recommend giving this guide provided by W3Schools a read as well. Now, going back to the index.js file, this is our root JavaScript file that loads our entire React app into the div container in /public/index.html. The first few lines contain import statements that load the React library, ReactDOM (this allows us to insert components into the DOM), and a regular CSS stylesheet. The next line import App from './App'; loads a React component titled App defined in the file App.js (note that we don't need to include the .js extension in the import statement), into this file. Next you will see something like the following code:

ReactDOM.render(<App />, document.getElementById('root'));

This line uses the render() function provided by ReactDOM to display our App component (which we have not yet gone over) inside the div tag with id "root" that we saw in index.html. Now, by adding all of our code and components inside this App component, this one line will take care of rendering our entire application.

App.js

[edit | edit source]

Next, navigate to and open /src/App.js. This is an example of a React component which we will discuss in more detail later. For now, we can think of components as custom HTML tags that return JSX-formatted code to render HTML elements. In App.js, we see a single function called App which returns some JSX elements. When we call ReactDOM.render() in index.js with the App component, it is this code that will be rendered inside our root div tag. We will gain a deeper understanding of this file later when we learn about components in React.

Hello World in React

[edit | edit source]

So far, we have used create-react-app to make a new application that runs on a local Node.js server. Now, we are going to start changing the React code to make the application our own. Keeping with tradition, the first thing we'll do is have the application display "Hello World" on the screen. In App.js, delete everything inside the <div> tag in the return block of the function. Replace it with a single header tag containing the text "Hello World". Your file should now look like this:

function App() {
  return (
    <h1>Hello World</h1>
  );

export default App;

If you still have your Node server running from earlier, go back to your browser to see the updated application. You may need to refresh the page. If you have closed the server, open a terminal/command prompt in the root folder of your project and run npm start to start the application again. You should see a blank page with the text "Hello World" in the left corner. If we want to add more HTML to our application, we can add that with JSX in App.js inside the return block. However, notice that we get an error if we try to add a second element, such as a paragraph tag after our header. React tells us the following:

Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?

This is happening because React only allows us to return a single HTML element in our components. To get around this, most components will return a parent container tag such as a div or span with multiple elements inside. For example, to fix our error, we can just wrap a div tag around our header and paragraph tags:

function App() {
  return (
    <div>
      <h1>Hello World</h1>
      <p>Goodbye</p>
    </div>
  );
}

export default App;

When we save and reload the page, we see the error is gone and both elements are displayed on screen.

Let's take a minute to recap what is happening in our application so far. The component in App.js returns some JSX code containing a div tag with a header and paragraph tag inside it. This component is then inserted into the div tag with id "root" created in index.html via the ReactDOM.render() function in index.js. We are able to reference the component in index.js after importing it and using the custom tag syntax <App /> in JSX. Notice the final line in App.js exports the component (the App() function in this case), allowing it to be imported and used in other JavaScript files and components in our application. All of this is run on a local Node.js server and displayed in a browser by running the npm start script via a terminal.

React Components

[edit | edit source]

Overview: Breaking a UI into Components

[edit | edit source]

Components are essential to any React application and represent a logical grouping of HTML content.[5] Most React applications in the wild will have tens or hundreds of components with many nested inside each other several layers deep. Therefore, it is important to keep components organized and make a plan before starting development to determine what components you will need to build. It may take some time to get used to the concept of components, but you can practice by looking at existing websites and trying to write down a list of React components that might be necessary to create the UI. This can work for almost any website, regardless of whether it uses React or not.

For example, consider the homepage of Wikiversity. We can see just by looking at the design that there are certain discrete sections of the webpage. For example, the navigation header with the link to log in is separate from the left side panel containing numerous links to different pages on the site. In React, these would likely be separate components. The side panel would likely consist of multiple components itself, possibly one for each of the link categories. If we scroll to the bottom of the page, we see a large panel titled "Wikiversity sister projects" containing a listing of related websites. Thinking in terms of React, this entire panel might be its own component with another component to represent each of the related sites. This sub component would contain a small icon image, an anchor tag linking to the site in question, and a paragraph tag providing a short description of the site. It's a good exercise to start thinking like this when you look at a webpage to get a better understanding of how to logically decompose a UI into separate components.

Types of Components

[edit | edit source]

In React, there are two main types of components: functional and class-based. While each type has its own use case, functional components are generally simpler, but slightly more limiting than class components[6] (this is less true now with the introduction of React hooks in functional components). So far, we have only seen an example of a functional component in App.js with our Hello World example. However, we haven't learned about class-based components, or how to create our own components from scratch.

Functional Components

[edit | edit source]

Functional components in React are simply functions, as the name implies. As noted, we have already seen an example of a functional component in the Hello World application. Let's take a look at that App component again and break it down:

function App() {
  return (
    <div>
      <h1>Hello World</h1>
      <p>Goodbye</p>
    </div>
  );
}

export default App;

First off, we notice that the file name App.js shares the same name as our functional component App. This is not a requirement, however it is considered best practice and prevents a lot of confusion later on when we want to use this component elsewhere. It's also best practice to create a new file for each component to keep them logically separate. Since App is a functional component, we define a function called App which returns a block of JSX code. Again, remember that each component can only return a single top-level element, so in order to return multiple elements we need to wrap them in a parent container (usually a div tag). For this simple component, a return statement is all that is necessary, but we are allowed to add additional JavaScript code prior to the return block if we need to do any logic or computation. Lastly, the line export default App; makes this functional component available to other files. This allows us to import and render it in index.js. When the component is rendered, the JSX returned by our App function is parsed into plain HTML and appended to the DOM. If it contains any other components, these are rendered/parsed as well.

Let's now try to create our own functional component. For this exercise, try to create a new functional component called Category that returns a category header, a list of at least three things in the category, and a horizontal line (you can use the <hr/> tag). Once you create the component, try to render it in place of the div tag we currently have in App.js. If you're not sure what to do, begin by creating a new file called Category.js and use App.js as a reference.

Category.js
import React from 'react';

function Category() {
    return (
        <div>
            <h1>Category Header</h1>
            <ul>
                <li>item 1</li>
                <li>item 2</li>
                <li>item 3</li>
            </ul>
            <hr/>
        </div>
    )
}

export default Category;
App.js
import Category from './Category';

function App() {
    return (
        <Category />
    )
}

export default App;

Your application should now look something like this:

Notice that we included the line import React from 'react'; in Category.js. This is not strictly necessary as more recent versions of React will automatically add this line for you, but it is good to get used to including it since it will be necessary for class-based components. All it does is allow the computer to understand JSX and translate it into code the browser can understand. Also take note of how we used our new Category component inside the App component like a normal self-closing HTML element. We can reuse this component to display multiple copies of itself by simply adding more of these <Category /> tags.

import Category from './Category';
function App() {
  return (
    <div>
      <Category />
      <Category />
      <Category />
      <Category />
    </div>
  );
}

export default App;

Now we have four copies of the component. This might be useful for displaying multiple categories and category data on a webpage. We will learn how to customize the data displayed by a component so that each category can have different data when we get to React props.

Class-Based Components

[edit | edit source]

Class based components use the class feature of JavaScript and are generally more powerful than functional components.[7] In general, most components you write will likely be class-based as they allow you to take advantage of React state to produce dynamic content (the recently introduced feature React hooks do allow you to accomplish this in functional components, but we will not be covering that in this course). Any functional component can be re-written as a class component, so let's go ahead and re-write our App component as a class-based component. First, we need to import React and our Category component:

import React from 'react';
import Category from './Category';

Next, we change the function declaration line into a class declaration:

class App extends React.Component {

We now have a class named App instead of a function, and it inherits from another class provided by React called Component. All class-based components must inherit from React.Component. Next, we must change our return block since we can't return from a class like we do a function. React provides us with a render() function that we can use inside our class to return the JSX that our component contains.

class App extends React.Component {
  render() {
    return (
      <Category />
    );
  }
}

The only difference here is that we return our JSX from inside the render function instead of a function component itself. Lastly, we need to export the class component so that it can be referenced in other files.

export default App;

All in all, this is the simplest example of a class-based React component. It just contains a function called render() which returns the JSX content of the component. The result is exactly the same as our previous functional component version of App.js.

As an exercise, try to re-write the Category component you created earlier as a class component. Use our new App.js component as an example.

Category.js
import React from 'react';

class Category extends React.Component {
    render() {
        return (
            <div>
                <h1>Category Header</h1>
                <ul>
                    <li>item 1</li>
                    <li>item 2</li>
                    <li>item 3</li>
                </ul>
                <hr/>
            </div>
        )
    }
}

export default Category;

CSS in React

[edit | edit source]

As with normal HTML, there are two main ways to include CSS in your React components. The first is with an external stylesheet.[8] If you notice in index.js, we are already using an external stylesheet called index.css which we import at the top of the .js file. Opening index.css, we can see there are various styles defined for the font and element spacing. To see what the application looks like with no styling, we can comment out the index.css import line in index.js and reload the page. The second way to add CSS to components is inline.[9] With plain HTML/CSS, we would write something like this to use inline styling to create a border and remove margins on a div tag:

<div style="margin: 0; border: 1px solid red;"></div>

This mirrors the syntax for writing CSS in an external file where each style ends in a semicolon. However, since we are now working with JSX instead of plain HTML/CSS, there are a few syntactical differences to be aware of. First, in JSX we have to wrap any JavaScript code or expression in curly brackets so the compiler knows to interpret it as JavaScript. And second, in React we are no longer passing a string to our style attribute, but a JavaScript object. So, to achieve the same inline styling in React, we have to write:

<div style={{margin: 0, border: "1px solid red"}}></div>

Note the differences between these two methods. First, we use two sets of curly braces instead of quotes. The outer pair is to indicate to the compiler that we are writing JavaScript code. The second is to represent an object. Remember that JavaScript objects are simply written as a list of comma separated key-value pairs surrounded by curly braces. This is why we no longer separate each style with a semicolon, but with a comma. Lastly, note the quotes around "1px solid red". This is necessary because we are using a string as the value of the border field in our JavaScript object whereas in normal HTML the entire style attribute is a string itself.

One final difference to note is that multi-word style attributes are written in camel case as opposed to dash-separated as in normal CSS. This is because we are no longer using the CSS attributes, but the JavaScript properties which use the camel case style. So, the following CSS

<p style="padding-left: 1em"></p>

Would be written in React as

<p style={{paddingLeft: "1em"}}></p>

Props

[edit | edit source]

As an exercise of everything we've learned so far, let's try to create a new component from scratch. This will be a functional component called TitleCard which will contain the name of a person as a heading, and their department of study (computer science, biology, math, etc.) with either (Student) or (Professor) next to it. Once you get it working, try to use some inline CSS styling to make it look a bit nicer.

Example of the TitleCard component
TitleCard.js
import React from 'react';

function TitleCard() {
    return (
        <div style={{marginLeft:"1em", marginTop: "1em", border:"2px solid black", display:"inline-block"}}>
            <div style={{marginRight: "1em", marginLeft: "1em"}}>
                <h2 style={{marginBottom:0}}>John Doe</h2>
                <p style={{marginTop:0}}>Engineering (Student)</p>
            </div>
        </div>
    )
}

export default TitleCard;

And in App.js we simply import the TitleCard component and replace the Category component in the return block.

Great! We now have a component to display information about a student or professor. Unfortunately, this component isn't really useful right now since we have hardcoded in the data that it displays. If we had multiple people we wanted to create title cards for, we would need a separate component for each just to change the name, department, and student/professor status. Luckily, React provides a feature to avoid this.

In plain HTML, elements can have attributes that can be used to change the way they look and function. For example, all tags can have an id and class attribute that help us apply CSS styles to particular elements or groups of elements. Most other elements such as the <input> tag can have numerous other attributes like type, value, and size. When these elements are added to the DOM to be rendered in a browser, these attributes are referred to as properties, or "props" for short. In React, our components are used as custom HTML elements and as such, they can also receive custom attributes called props. React props are how we can reuse the same component to display different information.[10] We can pass props to a component by choosing property names and providing their values just as we would with HTML attributes. Let's change the following line in App.js:

<TitleCard name="John Doe" department="Engineering" isStudent={true}/>

We are now passing all the information that may change from person to person into the TitleCard component as props. Note the curly braces surrounding the value of isStudent. Remember we need to surround all JavaScript expressions in JSX with curly braces. So, this is to tell the compiler to interpret the contents as a JavaScript expression since we want to use the JavaScript boolean literal 'true' instead of a string. Now, in order to accept and use these props in our component, we can simply add props as an argument to our function and access the fields of the JavaScript props object.

import React from 'react';

function TitleCard(props) {
    return (
        <div style={{marginLeft:"1em", marginTop: "1em", border:"2px solid black", display:"inline-block"}}>
            <div style={{marginRight: "1em", marginLeft: "1em"}}>
                <h2 style={{marginBottom:0}}>{props.name}</h2>
                <p style={{marginTop:0}}>{props.department} ({props.isStudent ? "Student" : "Professor"})</p>
            </div>
        </div>
    )
}

export default TitleCard;

There are a couple of things to note about the changes made to the highlighted lines above. First, we add props as an argument to the function so that the component can accept a props object. Next, instead of the person's name and department, we use the value in the name and department fields of the props object. These are the values we defined as properties in App.js. Remember we need to surround props.name and props.department with curly braces since they are JavaScript expressions, not JSX. Lastly, we use the JavaScript ternary operator for the props.isStudent field to decide whether to display "Student" or "Professor" next to the department name. This is a condensed if statement that is often used in React and essentially says 'if props.isStudent is true, then render "Student", otherwise render "Professor"'. This is a concept called conditional rendering where the data displayed depends on a condition (in this case whether props.isStudent is true or not).

If you have not done so yet, go ahead and refresh your application in the browser. You should see exactly the same thing as before, except now we can easily customize the data that TitleCard displays. Let's say we have a professor named Jane Doe who teaches in the Chemistry department and we want to add a title card for her. This is now as simple as adding a second TitleCard component in App.js with different props.

<TitleCard name="John Doe" department="Engineering" isStudent={true} />
<TitleCard name="Jane Doe" department="Chemistry" isStudent={false} />

You should see a new title card in your application with the data you passed in through props. Go ahead and play around a bit with this example until you feel comfortable with the concept of props. You can change the data passed in and even add new props to the component. Once you feel like you have a good grasp, try to add props to our Category component from before. Make the title and list items customizable through props.

Category.js
import React from 'react';

function Category(props) {
    return (
        <div>
            <h1>{props.title}</h1>
            <ul>
                {
                    props.items.map((item) => {
                        return <li>{item}</li>
                    })
                }
            </ul>
            <hr/>
        </div>
    )
}

export default Category;

I decided to pass in two props to my Category component. Title is a string representing the category title and items is a list of strings that represent the data in the category. Notice I used the map function in JavaScript to render each string in my items list as a separate list element. You may have created your component to accept each list item as a separate field in props, and then typed out a <li> element for each item in the component. This method does work, but it is not as flexible as using map since we need to manually add a new list element to the component if we want to expand the category list. The map function works on any list type in JavaScript and takes as argument a function. The example above accepts a lambda function that takes a single item (a string) from the list as argument and returns a list element containing that item. Map will apply this function for every item in the list and so we can easily expand the category list just by adding to the list we pass in to props. Our App.js looks like this:

import React from 'react';
import Category from './Category';

class App extends React.Component {
  render() {
    let items1 = ["Elephant", "Giraffe", "Snake"]
    let items2 = ["Apple", "Banana", "Carrot", "Bread", "Cereal"]
    return (
      <div>
        <Category title="Animals" items={items1}/>
        <Category title="Food" items={items2}/>
      </div>
    );
  }
}

export default App;

We defined two JavaScript lists in the render function of our class component before returning, and then pass those lists to the items field of props in our two Category components. If we add or remove strings from the two lists, the component will automatically update the information it displays.

React State

[edit | edit source]

So far, we have only learned how to create static React components whose data we can customize by passing in props. This is great for some purposes, but many times we want to create dynamic content on a webpage that responds and changes based on user input or some other event. In React, components handle their dynamic content in something called state.[11] Like props, state is an object that can contain multiple data fields. However, unlike props which is passed in to a component, state is defined and managed entirely within a component. It's also important to note that state can only be used in class-based components, and not in functional components. To introduce the concept of state, let's walk through a simple example of a click-counter component that displays the number of times a button has been clicked. To begin, we'll create a basic barebones class-based component called Counter using what we've learned so far.

import React from 'react';

class Counter extends React.Component {
  render() {
    return (
        <div style={{textAlign: "center"}}>
            <h2>0</h2>
            <button>Click Me!</button>
        </div>
    )
  }
}

export default Counter;

This component just consists of a render function which returns a div centered with inline CSS styling. The div contains a header for our click count and a button that will increment the number of clicks. If we render this component by importing and adding it to App.js, we will see that the button does not increase the number displayed above it. This is because we have hardcoded our header to display 0 and we haven't defined any action when the button is clicked. We'll fix both these issues by adding and updating state. The first step in adding state to a class-based component is to define a constructor at the very beginning of our class:

constructor(props) {
    super(props)
}

We accept one argument for any props that are passed in to the component and immediately call super() with those props to call the constructor of our parent class (React.Component). Calling super() must always be the first line in the constructor since it initializes this.props and allows us to use the keyword this later on.[12] Next, we define an object called this.state and add any fields we want with their initial values. Since we are only interested in tracking the number of times a button was clicked, I have just defined a single field in this.state called "clicks" set initially to 0.

constructor(props) {
    super(props)
    this.state = {
        clicks: 0
    }
}

Now, in place of the hardcoded 0 value in the header tag that our component returns, we want to display the value currently held in state. This looks similar to the way we use values held in props, except now we are accessing values held in this.state.

<h2>{this.state.clicks}</h2>

Great! We've just fixed the first issue with our app by letting state control the value stored in the header. However, we still need to increase that value by one every time the button is clicked. To do this, we define a function above our render function that uses the built in setState() function provided by React. There are two ways to use setState(). First, we can just simply pass in a new state object with the new fields and values we want. And second, we can pass in a lambda function that accepts the current state object as an argument, and returns a new state object.[13] This is useful when we want to update the state based on its current value, like incrementing a counter.

increaseClicks() {
    this.setState(prevState => {
        return ({
            clicks: prevState.clicks + 1
        })
    })
}

Note that we access the current value of our counter by using prevState.clicks inside our lambda function that is passed in to this.setState. If we wanted to use the simpler definition of this.setState that just accepts a new state object as an argument, we could do so with the following:

increaseClicks() {
    let newCount = this.state.clicks + 1
    this.setState({
        clicks: newCount
    })
}

This simply uses a variable to save one more than the current value of our counter by accessing this.state.clicks, and then sets the counter to that variable. Both of the above methods for setting state will work equivalently, what you use is based on preference. However, it should be noted that you must use setState() to update state and you should never attempt to change the state by accessing and setting this.state directly.[14] Now that we have our function that increases the click counter by one, we need to trigger that function to run when the button is clicked. We can accomplish this by setting the value of the onClick property of our button element to the function we just created. Remember we need to surround the function name with curly braces since we are writing a JavaScript expression inside JSX.

<button onClick={this.increaseClicks}>Click Me!</button>

Now, when the button is clicked, it will run the code we wrote in increaseClicks() to increment the counter we have stored in state. Every time a value in state is changed, the component is re-rendered, thus updating the value we see in our header on screen.[15] However, if you try to run our app now, you'll see that we get an error when we click the button that our object called this in increaseClicks() is undefined. To fix this, we need to add the following highlighted line to our constructor:

constructor(props) {
    super(props)
    this.state = {
        clicks: 0
    }
    this.increaseClicks = this.increaseClicks.bind(this)
}

This line can be a little bit confusing, but all it essentially does is ensure that our function increaseClicks() uses the same definition for this as the rest of our component. This is necessary because in JavaScript, functions don't always have a consistent definition of this and the object this refers to is dependent on how the function was called.[16] It's okay if this concept doesn't make complete sense, it's really just a quirk of the JavaScript language. Just remember to always bind your functions in the constructor if they need to access state. In any case, if we reload the application, we can see that our counter component now works as expected! Each time the button is pressed, our function is called and a new state object is passed in to setState() with the value of "clicks" incremented by one. Upon changing a value in state, React automatically re-renders (calls the render() function) our component to update its content.[17] Below is the entire completed Counter.js component.

import React from 'react';

class Counter extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            clicks: 0
        }
        this.increaseClicks = this.increaseClicks.bind(this)
    }

    increaseClicks() {
        this.setState(prevState => {
            return ({
                clicks: prevState.clicks + 1
            })
        })
    }

    render() {
        return (
            <div style={{textAlign: "center"}}>
                <h2>{this.state.clicks}</h2>
                <button onClick={this.increaseClicks}>Click Me!</button>
            </div>
        )
    }
}

export default Counter

Lifecycle Methods

[edit | edit source]

So far, we have seen that class components must contain a render method, and they can optionally include a constructor and any number of other custom functions that we define. However, React also provides various "lifecycle methods" that execute a different points in the lifecycle of a component. The three most commonly used lifecycle methods that we will be learning about are componentDidMount(), componentDidUpdate(), and componentWillUnmount().

The first stage in the lifecycle of a component is mounting in which the component is added to the webpage's DOM. Here, the component's constructor is first called, followed by its render method, and lastly the componentDidMount() method. The next stage is updating and occurs when something in a component changes, thus forcing it to update. This could be a change in the component's state, props, or a manual action to force an update. Regardless, the render method is the first to be called, followed by componentDidUpdate(). Lastly, a component unmounts, meaning that it is removed from the page's DOM. This occurs often with conditional rendering where a state change causes a component to stop rendering. Just before the component is removed, the componentWillUnmount() lifecycle method is called.[18]

componentDidMount()

[edit | edit source]

This is possibly the most important and commonly used lifecycle method in React. It is useful to start an action such as performing an API call as soon as a component mounts to the DOM. To illustrate this method, we will create a component that makes a fake API call upon mounting and then displays the returned data. Create a class component called Temperature with a constructor and render method. It should display a city name passed in through props, and the current temperature in that city. For now, just create a temperature field in state initialized to a placeholder value.

Temperature.js
import React from 'react';

class Temperature extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            temperature: 0
        }
    }

    render() {
        return(
            <h2><b>It is {this.state.temperature}°F in {this.props.city}</b></h2>
        )
    }
}

export default Temperature

So far, this is nothing new. But what if we wanted to return the actual temperature in a city by calling an API? Well, the componentDidMount() lifecycle method is the perfect place for this since it runs just after the component is added to the DOM. Let's add the code for that now just after the constructor:

componentDidMount() {
    setTimeout(() => {
        this.setState({
            temperature: 63
        })
    }, 1000)
}

All we're doing is setting a timeout delay to simulate making an API call, and then setting the temperature value in state to a random value (this would be what the API returns). If you refresh the application, you'll see that it initially displays your placeholder value for the temperature, then updates to 63 degrees after one second. Ideally, we either don't want to display anything before the component receives its temperature data, or show some loading text while we wait to receive an API response (or wait for the timeout to finish in our fake example). To accomplish this, we can add a second variable to state that tracks whether or not we have received our data yet and then use some conditional rendering.

import React from 'react';

class Temperature extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            temperature: 0,
            loading: true
        }
    }

    componentDidMount() {
        setTimeout(() => {
            this.setState({
                temperature: 63,
                loading: false
            })
        }, 1000)
    }

    render() {
        return(
            <h2>
                {
                    this.state.loading ? 
                        "Loading..." : 
                        <b>It is {this.state.temperature}°F in {this.props.city}</b>
                }
            </h2>

        )
    }
}

export default Temperature

We added a loading field in the constructor initially set to true, since we don't yet have our temperature data when the component mounts. Then, in componentDidMount(), we set the loading field to false after our one second timeout simulating an API call completes. Lastly, in the render method we used conditional rendering based on the loading field to display some text if this.state.loading is true, and the temperature data if it is false. For clarity, I expanded the ternary operator to multiple lines, but this is not necessary.

See Also

[edit | edit source]
  1. "React – A JavaScript library for building user interfaces". reactjs.org. Retrieved 2021-06-02.
  2. "React – A JavaScript library for building user interfaces". reactjs.org. Retrieved 2021-06-02.
  3. "Create a New React App – React". reactjs.org. Retrieved 2021-06-02.
  4. "Folder Structure | Create React App". create-react-app.dev. Retrieved 2021-06-02.
  5. "Components and Props – React". reactjs.org. Retrieved 2021-06-02.
  6. "Components and Props – React". reactjs.org. Retrieved 2021-06-02.
  7. Arif, Hussain (2020-06-07). "Class Based Components in React.Js". Medium. Retrieved 2021-06-02.
  8. "Styling and CSS – React". reactjs.org. Retrieved 2021-06-02.
  9. "DOM Elements – React". reactjs.org. Retrieved 2021-06-02.
  10. "Components and Props – React". reactjs.org. Retrieved 2021-06-02.
  11. "State and Lifecycle – React". reactjs.org. Retrieved 2021-06-02.
  12. "React.Component – React". reactjs.org. Retrieved 2021-06-02.
  13. "React.Component – React". reactjs.org. Retrieved 2021-06-02.
  14. "React.Component – React". reactjs.org. Retrieved 2021-06-02.
  15. "Component State – React". reactjs.org. Retrieved 2021-06-02.
  16. "Understanding JavaScript Function Invocation and "this"". Katz Got Your Tongue. 2011-08-11. Retrieved 2021-06-02.
  17. "React.Component – React". reactjs.org. Retrieved 2021-06-02.
  18. "React Lifecycle Methods diagram". projects.wojtekmaj.pl. Retrieved 2021-06-02.