← Home

A Complete Beginner's Guide to React: Hooks Edition

A few years ago, I wrote an introductory React tutorial based on a workshop I taught. Since that point in history, I've taught hundreds of new web developers React, and the library has evolved significantly. I thought it was time to come back to the fundamentals and discuss learning React from absolute zero in the era of Hooks.

Some pre-requisites for the tutorial in this blog post: HTML, CSS, and JavaScript fundamentals.

React is a JavaScript library which was written by Facebook in 2013. It speeds up and simplifies the frontend development process. It has many key features that aid in the development of user interfaces, such as JSX, components, and the virtual DOM. It's almost inescapable in the current era of frontend development. In this tutorial, we'll learn about the React fundamentals, use Create React App to scaffold a project, build out an app with React state and props, and then deploy it to AWS Amplify.

Components

You may have learned very early on in your programming journey to "separate concerns" or put your HTML, CSS, and JS in separate files. React flips this on its head. We will instead break our webpages into chunks called components and write all of their display and logic code all in one. This will increase the modularity of our user interfaces, and we can compose components in infinite combinations and reuse them over and over again.

In many web pages, you will have similar user interface components over and over again -- think, for example, of Facebook's like button. It's on posts, videos, and pictures. If you were using standard HTML, each one of those buttons would need to be written separately, given a class to add styling to them, and then JavaScript code would need to be written to implement their behaviors. If you needed to update these buttons, code would likely need to be changed in many places.

React's component-based architecture allows us to instead reuse that button over and over again and only update code in one place if needed.

Let's see how a Facebook status may be broken down into components:

Facebook status with components highlighted

We can also have subcomponents which are components within a parent component.

components within components

For example, the three buttons at the bottom of the status could be broken into their own components.

There are a bunch of ways you could break down these components and subcomponents depending on your application's needs.

Installation && Setup

First, install Node.js. If you haven't encountered Node.js, it is a runtime that allows you to write JavaScript outside the browser. In this case, we'll be using it to make development on our React application as easy as possible.

Once you have Node installed, open up your command line and run: npx create-react-app color-switcher. Heads up, this command may take a few minutes to run.

  • npx is a tool that comes with Node that allows you to run commands without installing them first.
  • We are using it to run create-react-app which does what the name implies! It scaffolds a React application for us and sets up Babel and Webpack, two super important tools we'll come back to later in the tutorial.
  • Finally color-switcher is the name of our application, you will need to re-run the npx create-react-app your-app-name command for each app you build.

Once your app is created cd into the directory that create-react-app created for us.

cd color-switcher

Create React App Generated Files

Open up the directory in your text editor of choice, and check out the files that were created.

Create React app generated file tree

There are a bunch! But don't be intimidated, let's do a quick run-through of the files create-react-app generated. The ones highlighted are the important ones for us:

  • node_modules/ - the Node modules we are using for our project, essentially other people's code that's going to make our life easier.
  • public/
    • favicon.ico - the icon that shows up when you favorite a site
    • index.html - the file our JavaScript attaches to
    • logo.png files - different icons for our app
    • manifest.json - allows us to make our app into a progressive web app, we won't make our app into one today.
    • robots.txt - declares the rules for bots on our site
  • src/
    • App.css - Where our App component's styling will go
    • App.js - Where our App component will be written
    • App.test.js - A file where you can write automated tests for your App component
    • index.css - A file for global styles for your application
    • index.js - Configuration for your React application
    • logo.svg - a React logo
    • serviceWorker.js - code to configure
    • setupTests.js - configuration for automated testing
  • .gitignore - files you want to keep local and hide from git
  • package.json - holds project metadata
  • package-lock.json - auto-generated file to track dependencies

There are a bunch of files created, but we only need to worry about a very few of them.

The index.js File

Let's break down what's in the index.js first:

import React from 'react' - import the React library. We need this in every file where we use JSX syntax. import ReactDOM from 'react-dom' - ReactDOM allows us to use React in the browser import './index.css' - apply the global styles import App from './App' - import the App component

Now for the important code! We are going to take our <App> component and attach it to an element with the id #root. You can find that element in the public/index.html file. This allows our React code to actually render on the page.

React utilizes the virtual DOM, which is a virtual representation of the DOM that you would normally interact within Vanilla JavaScript or JQuery. This reactDOM.render renders this virtual DOM to the actual DOM. Behind the scenes, React does a lot of work to efficiently edit and re-render the DOM when something on the interface needs to change.

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

The <React.StrictMode> component is a tool that will give you extra warnings about issues that may exist in your code.

Again, we'll just ignore the service worker code, it goes above and beyond what we need to do.

You don't need to change anything in the index.js file, I just wanted to run through it since it is so key to making our application work.

A React Hello World

Now for the fun part, writing the code! Open up your App.js file. This will be our top-level, or root, component for our application.

Inside that file we already have some code written. We are again importing React (which we need to do in every file that we use React in), the React logo, and the CSS file specific to this App component.

We also have a function, App that returns a bunch of what looks like HTML -- this is actually JSX.

Finally, we are exporting the component so we can import it into other files, in this case, our index.js that was generated for us.

Let's go ahead and remove the logo import and change the JSX code so that it only returns <h1>Hello, World</h1>

import React from 'react'
- import logo from './logo.svg'
import './App.css'

function App () {
  return (
+    <h1>Hello world!</h1>
  )
}

export default App

JSX is an extension of JavaScript that allows you to write what looks like HTML directly in your JavaScript code. You can't natively use JSX in the browser, but we will use a library called Babel to transpile (or convert) our JSX into regular JavaScript so that the browser can understand it. JSX is actually optional in React, but you'll see it used in the vast majority of cases.

Okay, now you've written your first React code, but how do you see the output? Go back to your CLI and run npm run start. A webpage should pop up that displays your React app. It will hot reload, so every time you change your code and save those changes, your application will show those changes automatically. In order to exit out of the server, you can press ctrl + c. It may be helpful to have two terminal windows or tabs open while you're developing React apps because you can't write additional commands in the session where the server is running.

React Color Switcher

First, we're going to build a color picker application -- the background of the page will change color based on a button the user selects.

click buttons and background changes

The first step is to build out the static user interface. First, we'll add an enclosing div which will be the top-level element of our React application. Then, we'll add another div inside of that one that will allow our content to be centered on the page. We'll also add a header to title our page and three buttons. We are also going to add some className attributes to our elements. In JavaScript, a class is for creating classes in object-oriented programming, so, React can't use the word class to add class names for styling groups of elements. So, it uses className instead.

We will add the following classNames to our elements:

import React from 'react'
import './App.css'

function App () {
  return (
    <div className='react-root'>
      <div className='centered'>
        <h1>Color Picker</h1>
        <button className='red'>red</button>
        <button className='blue'>blue</button>
        <button className='yellow'>yellow</button>
      </div>
    </div>
  )
}

export default App

Since this tutorial is focused on React, we'll just copy some CSS code into your App.css. Remove what's in there and replace it with:

html, body, #root, .react-root {
  height: 100%;
  width: 100%;
  background-color: white;
  color: black;
  display: flex;
  justify-content: center;
  align-items: center;
}

.centered {
  text-align: center;
}

button {
  padding: 10px;
  margin: 5px;
  border: 2px solid white;
  color: white;
  font-size: 20px;
}

.red {
  background-color: red;
  color: white;
}

.blue {
  background-color: blue;
  color: white;
}

.yellow {
  background-color: yellow;
  color: black;
}

Now, your app should look like this:

three buttons with different colors

Now we need to actually make it do something!

Any variables we want to have changed while our application is running need to be stored in state. This will cause React to automatically update our component's appearance each time a state variable updates.

React State

In order to utilize state, we will import the useState hook from React.

Hooks are new to React -- they were introduced near the end of 2018. React looks very different from when I learned it five years ago. ES6 wasn't fully implemented yet, so we wrote components using objects and the React.createClass function. Then there was the era of JavaScript classes, and most recently, React has implemented Hooks, which allow us to write components using just functions. This makes React's syntax simpler and less verbose. According to the React docs, "Hooks are functions that allow us to 'hook into' React features."

In our App.js component, we will update our first line of code.

+ import React, { useState } from 'react'
import './App.css'

The useState hook takes one argument: what the initial value of state will be. It then returns two values in an array. The first is the value of the state variable, the second is a function that will allow us to update state. We will use array destructuring to set both of the items returned to their own variables.

import React, { useState } from 'react'
import './App.css'

function App () {
+  const [color, setColor] = useState('')
  return (
    <div className='react-root'>
      <div className='centered'>
        <h1>Color Picker</h1>
        <button className='red'>red</button>
        <button className='blue'>blue</button>
        <button className='yellow'>yellow</button>
      </div>
    </div>
  )
}

export default App

If you console log each item, you'll see color is an empty string because we provided useState the argument ''. If you changed that empty string to 'blue' then color will then store the value blue! setColor is a function, which we will use to update the color variable.

Now, we need to add an event listener so that when a user clicks on our buttons, the color stored in state updates.

First, we will display the current value of color on the interface. We can do this by writing the color variable in curly braces, this tells React that any code inside the curlies is JavaScript code.

We will also add an onClick attribute to our first button, after that onClick we will add a function that will run when the event fires. This is how we write event listeners in React. For now, we will just console.log('clicked').

import React, { useState } from 'react'
import './App.css'

function App () {
  const [color, setColor] = useState('')
  return (
    <div className='react-root'>
      <div className='centered'>
        <h1>Color Picker</h1>
+        {color}
+       <button className='red' onClick={() => console.log('clicked')}>
          red
        </button>
        <button className='blue'>blue</button>
        <button className='yellow'>yellow</button>
      </div>
    </div>
  )
}

export default App

Check out your JavaScript console and see what's happening!

Now we'll change the event listener function to instead change the color state variable. We can do so by using the setColor function that useState gave us.

<button className='red' onClick={() => setColor('red')}>
  red
</button>

Now, you can see that when you click on the button the word "red" displays on the page! Now let's make both of the other buttons work as well.

<button className='blue' onClick={() => setColor('blue')}>blue</button>
<button className='yellow' onClick={() => setColor('yellow')}>yellow</button>

The last thing that we need to do is actually change the color of the page instead of just displaying the color's name on the page. In our CSS file, we already have three classes for our colors -- yellow, red, and blue. What we need to do is add those classes onto our react-root element so that it changes color to match our color variable. We need to make our className take JavaScript code instead of just a string, and then we will use string interpolation to add our color class to the element.

<div className={`react-root ${color}`}>

Our final code should look like this:

import React, { useState } from 'react'
import './App.css'

function App () {
  const [color, setColor] = useState('')

  return (
    <div className={`react-root ${color}`}>
      <div className='centered'>
        <h1>Color Picker</h1>
        <button className='red' onClick={() => setColor('red')}>red</button>
        <button className='blue' onClick={() => setColor('blue')}>blue</button>
        <button className='yellow' onClick={() => setColor('yellow')}>yellow</button>
      </div>
    </div>
  )
}

export default App

React Props

Now we've used some of React's most important features: JSX and state. There are two more that I want to show you: components and props.

Right now we are actually using a component: App. But, we want to make our components small and reusable. Right now, our buttons follow a pattern. Each displays text, has a className, and has an onClick event. We will make a second ColorChangeButton component so that we can reuse as much code as possible, and if we want to update the buttons in the future we can do so more easily.

The first step is to create another file in your src/ folder called ColorChangeButton.js.

files with colorChangeButton.js added

Now, we will create a second React component in this file.

// ColorChangeButton.js
import React from 'react'

function ColorChangeButton () {
  return (
    <button>Hi!</button>
  )
}

export default ColorChangeButton

We'll now go back to our App.js and import our ColorChangeButton:

// App.js

import React, { useState } from 'react'
import './App.css'
+ import ColorChangeButton from './ColorChangeButton'

In our JSX code, we will create three instances of our ColorChangeButton.

 // App.js
  return (
    <div className={`react-root ${color}`}>
      <div className='centered'>
        <h1>Color Picker</h1>
+        <ColorChangeButton />
+        <ColorChangeButton />
+        <ColorChangeButton />
        <button className='red' onClick={() => setColor('red')}>red</button>
        <button className='blue' onClick={() => setColor('blue')}>blue</button>
        <button className='yellow' onClick={() => setColor('yellow')}>yellow</button>
      </div>
    </div>
  )

Boom! Now you should have three more buttons that show up on the page that all say Hi!. This is how we create and include a second component in React.

But, right now our components are pretty boring. They all say the same thing. We want these to eventually replace the three color-changing buttons we have written, so we need to allow our button to be a different color and to have different text.

React uses unidirectional data flow, which means we can only pass data from a parent component to a child component. We will use props to pass data from one component to another.

 // App.js
  return (
    <div className={`react-root ${color}`}>
      <div className='centered'>
        <h1>Color Picker</h1>
+        <ColorChangeButton color='red' />
+        <ColorChangeButton color='blue' />
+        <ColorChangeButton color='yellow' />
        <button className='red' onClick={() => setColor('red')}>red</button>
        <button className='blue' onClick={() => setColor('blue')}>blue</button>
        <button className='yellow' onClick={() => setColor('yellow')}>yellow</button>
      </div>
    </div>
  )

In our parent component, App, we can use what looks like an HTML attribute to send props. In this case, color is the name of our prop and then the value comes after the equals sign, 'red' for the first component, 'blue' for the second, and 'yellow' for the third.

Now, we need to use those props in our child component. Switch over to ColorChangeButton.js. First, we will make our function take the parameter props.

// ColorChangeButton.js
function ColorChangeButton (props) {
  ...
}

Then, you can console.log props before the return to see what's there:

{ color: 'red' } { color: 'blue' } { color: 'yellow' }

It's an object! React combines each prop we send from the parent component into an object with each key and value in the child. So, to access our color in our child component, we would do props.color. Let's make our button display our color as its text and also add the color as a class to the button so that the correct color displays.

// ColorChangeButton.js
import React from 'react'

function ColorChangeButton (props) {
  return (
+    <button className={props.color}>{props.color}</button>
  )
}

export default ColorChangeButton

Now our buttons look the way they are supposed to! The last thing that we need to do is make the click event work. In our App.js, we wrote this code to change the current color:

<button className='red' onClick={() => setColor('red')}>red</button>

The one issue we have is that setColor is defined in our App component, so we don't have access to it ColorChangeButton. Good news though: we have a way to pass data from a parent component to a child component that we learned in the past step: props! Let's pass the setColor function down as a prop to our ColorChangeButton component.

I'm also going to delete our three original buttons since we no longer need them.

 // App.js
  return (
    <div className={`react-root ${color}`}>
      <div className='centered'>
        <h1>Color Picker</h1>
+        <ColorChangeButton color='red' setColor={setColor} />
+        <ColorChangeButton color='blue' setColor={setColor} />
+        <ColorChangeButton color='yellow' setColor={setColor} />
      </div>
    </div>
  )

Now, if you go back to the ColorChangeButton and console.log what the props are, you'll see that you have a second item in the object, for example:

{
  color: "red"
  setColor: ƒ ()
}

Let's use that setColor function:

function ColorChangeButton(props) {
  return (
+   <button className={props.color} onClick={() => props.setColor(props.color)}>
      {props.color}
    </button>
  )
}

export default ColorChangeButton

Now each button should work as expected! This pattern of passing the state change function down from parent to child components is called inverse data flow. It allows us to circumvent the unidirectional data flow nature of React.

Deployment

Amazing, now our application is complete. One small problem though: our application is only accessible locally, meaning that we can't send the localhost URL to friends for them to see the application. We need to deploy our application so that anybody on the web can see it. We will use AWS Amplify to do so with just a few clicks.

First, we need to push this tutorial code to GitHub by:

  • creating a new repository by clicking the plus button on the top right of the page.
  • choose a repository name and click create repository
  • Then, copy the commands from …or push an existing repository from the command line and run them in your terminal.
  • Run git add . && git commit -m "tutorial complete" && git push in your terminal to send your latest changes.

Then to get it deployed:

  1. Create an AWS account if you don't already have one.

  2. Navigate to the Amplify Console

  3. Click on the orange connect app button.

  4. Choose GitHub in the From your existing code menu, and click continue

Amplify interface with different remotes

  1. Type in the name of your GitHub repo you just created (it should autofill!) and then click next

Amplify interface with name of repo

  1. The build settings will auto-populate, and so you can just click next on the Configure build settings
  2. Click Save and deploy.

For the first 12 months of your AWS account existing, Amplify has a free tier that will most likely cover your hosting. After that, here is more information about pricing!

It may take a few minutes for your app to build, but once it does you will get a URL that anyone can visit! You can visit mine at this url.

Next Steps

This tutorial took you through the basics of React data flow, components, and project setup. Here are some awesome resources for learning more:

In addition, I did a live stream with the content from this tutorial if you'd prefer to view it that way. If you extend what you built, please share it with me on twitter!

Be the first to know about my posts!

Share this post with a friend!

LinkedIn
Reddit