🏋🏽‍♂️ Hooks Workout #1: Your First React Hooks Application

🏋🏽‍♂️ Hooks Workout #1: Your First React Hooks Application

Hey,

As we covered in the introductory lesson, I’m gonna show you around building stuff with hooks, while also covering any fundamentals you need to know.

Ready?


This lesson is a part of the Hooks workout series:

  1. Your First React Hooks Application 👈🏽 You're here  
  2. Advanced Hooks
  3. Fetching Data with Hooks
  4. Custom Reusable Hooks
  5. Moving into Production

Head over to https://codesandbox.io/s/new where you’ll find a bootstrapped react project based on create-react-app.

It should look like this:

I told you I wasn't playing about. These lessons are hands-on!

Now, delete everything within the div with className App, and write the following:

 <h1> 0 </h1> 

An h1 element with the number 0.

If you did that, you should now have the number 0, rendered in the display screen on the right.

Here’s what mine looks like:

Zero displayed 

This is far from being exciting, but here’s where the fun part begins.

The goal of this lesson is to build your first application with hooks – a simple application before we delve into more interesting use cases.

So, what’s the functionality of this app?

The Application

The goal of the application is to be able to click anywhere on the screen and have the number displayed incremented by one.

How do you think we may solve this?

Well, with good old Javascript!

You must remember window.addEventListener. The addEventListener method attaches an event listener to a specified element.

So, the solution we seek may look like this:

window.addEventListener("click", updateCount) 

Right?

Whenever the window is clicked, we run a callback function e.g., updateCount.

Introducing the useEffect Hook

The App component is a functional component. We can’t just invoke the window.addEventListener method within the function.

So, how do we go about this?

If you remember writing React components as classes, you must remember the concept of lifecycle methods. Within some of these lifecycle methods, you could perform side effects.

In loose terms, a side effect is simply a change triggered outside the current system.

In functional components, to perform side effects, you use the useEffect function.

So, where do we place the the window event listener call? Yes, within the useEffect hook.

Let me show you how.

Currently, here’s what you have in the application:

// before
import React from 'react'

function App() {
  return (
    <div className="App">
      <h1> 0 </h1>
    </div>
  );
}

Here’s how to introduce the useEffect hook:

// now
import {useEffect} from 'react'

function App() {
  useEffect(() => {
    
  })
  // the return statement comes after ... 
}

Do you see what’s happened?

Go to the first line in your code and import the useEffect hook as shown below:

// before
import React from 'react; 

// now 
import React, {useEffect} from "react";

Now, go ahead and introduce the useEffect hook within the App component.

useEffect(() => {
    
})

Note that useEffect is just a function that takes in a function argument.

Wait …

Have you written out the useEffect implementation by hand? Or did you copy-n-paste? Or much worse, did you fail to take any action?

It’s really important you’re writing all of this by hand. Remember our pact?

I’ll teach you to build stuff, but you must do the work by actually working your hands. Write the code! 😉

Now, I believe you’ve gone ahead to ... write the code.

After doing that, we need to introduce the actual window event listener. Here’s how:

useEffect(() => {
    const updateCount = () => console.log("count updated") 
    window.addEventListener('click', updateCount)
})

Go ahead and write that. Don’t copy-n-paste!

Do you understand what’s going on there, within the function passed to useEffect you can perform any side effect you want.

Here we define a simple function that logs the text count updated. Then we set an event listener on the window object.

Whenever the window is clicked, a log should now appear in the console!

Try it out.

You’ll find the console tab below the display area. Click it to open the console panel, then click around the display area to trigger the click event listener!

Did you get that?

You’ve done a great job so far. However, this isn’t the kind of app you want to show off to a friend. Let’s make it more fun.

We don’t want to just log texts to the console. Whenever we click the display area we want to update the value of the number displayed on the screen.

How may we do that?

Introducing the useState hook

To update the value of the displayed number, we need to keep track of a state variable within the functional component, App.

Whenever you need to introduce local state into a functional component, the useState hook is the right choice.

Now, you’ll introduce the useState hook into the app as shown below:

function App() {
  // look below 👇
  const [count, setCount] = useState(0)

  useEffect(() => {
    const updateCount = () => console.log("count updated") 
    window.addEventListener('click', updateCount)
  })

}

Go ahead and write const [count, setCount] = useState(0), within the App component.

Well, that’s not going to work until you import the useState hook.

So, go to line 1 in your code, and import useState as shown below:

// before 
import React, { useEffect } from "react";

// now 
import React, { useEffect, useState } from "react";

Now, you may have questions about what const [count, setCount] = useState(0) means .

The useState hook is just a function that is invoked with an initial state value. e.g. useState(initialValue).

In this case, we’ve invoked useState with the initial value of 0.

The interesting bit is that, when you invoke useState it returns an array of two values.

//invoke useState 
useState(initialValue);

//returns similar:
[value1, value2]

This is the consistent signature for useState. It’ll always return 2 values within an array.

The first value, value1, represents the state value (at first this is the same as the initialValue), and the second value, value2 represents a function you can use to update the state value.

Here’s the line I asked you to write:

const [count, setCount] = useState(0);

The syntax, const [count, setCount] is called the array destructing syntax. This way we immediately assign the values returned within the array to the variables count and setCount.

// longer solution
const resultArray = useState(0)
const count = resultArray[0]
const setCount = resultArray[1]

// much neater with array destructuring 
const [count, setCount] = useState(0)

count represents value1, the state value, and setCount, the function to update the count state variable.

The syntax might look strange if you’re absolutely new to it, but it’s a real time saver. You’ll get used to it soon :)

Now, instead of rendering the static number 0 within the App component, go ahead and change that to the count state variable.

Here’s how:

// the return statement of App

// before:
<div className="App">
  <h1> 0 </h1>
</div>


// now: 
<div className="App">
   <h1> {count} </h1>  {/** 👈 look here ** }
</div>

Now, instead of the number, 1, you should now see the initialValue of count, which is 0.

Is that working fine for you?

I hope so!

Now, the next thing to be done is to actually update this count state variable whenever the display is clicked.

You have to remove the console.log statement and invoke setCount.

Here’s how:

// before 
useEffect(() => {
    const updateCount = () => console.log("count updated");
    window.addEventListener("click", updateCount);
  });

// now 
useEffect(() => {
    // look here 👇
    const updateCount = () => setCount(count + 1);
    window.addEventListener("click", updateCount);
  });

See what’s going on there?

Instead of the console.log call, we now invoke setCount with a new value, count + 1.

Let me explain a little further.

The second value returned from the useState call is an updater function. It is a function used to update the value of the state variable.

It is invoked like this: updaterFunction(newStateValue)

You invoke the function with a new state value, that’s all. React will note the updater function call and update the state variable with the new state value.

So, when we call setCount(count + 1), this updates the count variable to count + 1.

Got that, huh?

Go ahead and implement that in your code. Then click the display area to see the number update as you click!

Multiple State Values

There are cases where you need multiple state values. Don’t fret, you can call useState as many times as you want. React handles each useState call separately — without any conflicts in state updates.

Let’s say you wanted to update the app to show the time a user clicked. Now, we need to keep track of a second state variable that holds the time value.

Here’s how you could do that by adding a new useState call below the first.

// before 
const [count, setCount] = useState(0);

// now 
const [count, setCount] = useState(0);
const [time, setTime] = useState(new Date());

Go ahead and write that.

You see, it’s the same signature as before.

A new state variable, time and a new updater function, setTime — remember you can name these values as you want. time and setTime seemed appropriate to me.

Also note that the initial value passed to the second useState call is a new Javascript Date object - new Date(). This returns a new Date object which we’ve called time.

By using the Date object, we also get access to the beautiful methods the Date object expose e.g. Date.toLocaleTimeString

And now, you can go ahead to display the current time in the app as follows:

// before: 
<div className="App">
   <h1> {count} </h1>
</div>


// now
<div className="App">
   <h1> {count} </h1>
    { /** look here 👇 **/ }
   <h2>{time.toLocaleTimeString()}</h2>
</div>

Easy-peasy!

One more thing. Whenever the user clicks the screen, we want to update the time as well.

Let’s do so:

// before 
useEffect(() => {
    const updateCount = () => setCount(count + 1);
    window.addEventListener("click", updateCount);
});

// now 
useEffect(() => {
    // look here 👇
    const updateCountAndTime = () => {
      setCount(count + 1);
      setTime(new Date());
    };
    // look at the name of the function 👇
    window.addEventListener("click", updateCountAndTime);
});

All we’ve done is invoke the second state updater, setTime with a new Date. The callback passed to addEventListener has also been renamed from updateCount to updateCountAndTime.

Now, the app should work as expected!

Go ahead and implement that.

Unsubscribing Effects

If you remember from vanilla Javascript, event listeners should be unsubscribed to prevent memory leaks.

The useEffect hook allows for unsubscribing from subscriptions. The way it does this is, if you return a function from the effect function, it’ll invoke that function to unsubscribe from the effects you might have subscribed to.

Here’s how:

// before 
useEffect(() => {
    const updateCount = () => setCount(count + 1);
    window.addEventListener("click", updateCount);
  });

// now
 useEffect(() => {
    const updateCountAndTime = () => {
      setCount(count + 1);
      setTime(new Date());
    };
    window.addEventListener("click", updateCountAndTime);
	// look here 👇
    return () => {
      window.removeEventListener('click', updateCountAndTime)
    }
});

You see how we return a function?

Within that function I then use the removeEventListener method to unsubscribe from the event listener.

Go ahead and implement that in your app.

It is worth mentioning that every time a state value changes, your component re-renders. By default, with every re-render, the function within useEffect is also run.

Every time the useEffect function is run, it first calls the unsubscribe function before subscribing to any effects you may have.

The End

We’ve come to the end of the first lesson, however, because I hate ugly apps, go ahead and add the following css to the styles.css file of your app:

body {
  background: #2980b9;
  color: #fff;
}

h1 {
  font-size: 10rem;
}

Here’s the final version of my app:

What does yours look like? Feel free to comment with a screenshot if you want 😊 Who doesn't love a screenshot!

See you in the next lesson where we’ll discuss some more advanced concepts!

Say No to Javascript Fatigue

At Devcher, we're changing how you stay up to date with Javascript by building the principal destination for short-form developer mobile video. Try Devcher.

Join Devcher: The Anti Fatigue Community

Comments

Become a Devcher member below to join the conversation. As a member, you will also receive new posts by email (you can unsubscribe at any time).