Thanks to visit codestin.com
Credit goes to medium.com

Sitemap

React Hooks Tutorial — Learn by Building

9 min readJan 27, 2019
Press enter or click to view image in full size

Hooks

Hooks are an upcoming feature of React. This update is sending shockwaves of excitement throughout the React community.

In short, you can now use state, and other React features, within your functional React components. So, you don’t need a class to use state!

In this article

I’ll show you how to use React hooks to build an application. For some of the code, I’ll also show you the traditional non-hooks approach. That way, you can see the difference that React hooks is making.

If you’re a newcomer to React, then hooks are another foundational concept to learn.

If you’re an experienced React developer, you don’t need to forget everything you learned about React. In fact, hooks will enhance your ability to write clean React code overall.

The app you’ll build

For this Hooks tutorial, you’ll build an app called Type Race. This mini game tracks how quickly you can type out a snippet of text. It’s pretty addicting as playing the game helps you measure your typing ability, and improve your keyboard dexterity!

A demo: https://davidtkatz.com/#/typerace

Completed code for this tutorial: https://github.com/15Dkatz/typerace-hooks

Set up the project

Set up the react hooks project with npx, which will trigger the create-react-app tool:

npx create-react-app typerace-hooks

Navigate into the typerace-hooks directory, and then add [email protected] and [email protected]. These versions support the new hooks functionality:

yarn add [email protected] [email protected]

Make sure the typerace-hooks/package.json file lists the react and react-dom dependencies as versions 16.8.0-alpha.1.

"react": "@16.8.0-alpha.1",
"react-dom": "@16.8.0-alpha.1",

Hook State

The biggest part to learn about hooks is the new way of declaring state. So as the first part of this app, let’s set up a component with state, the hooks way.

At its center, Type Race will display an input field. This will track what the user has typed within the component state. To set it up, let’s refactor the App.js component, and reduce it down to a title for the application, and an input field to track the state:

import React from 'react';const App = () => {
return (
<div>
<h2>Type Race</h2>
<input />
</div>
)
}
export default App;

And now, let’s use a React hook. We’ll add in some state to track the user input. To do so, we’ll import the useState hook from React at the top of the App.js file:

import React, { useState } from 'react';

Then we’ll apply the useState hook at the top of the App component function, to set up state functionality for the userText:

const App = () => {
const [userText, setUserText] = useState('');

There are three aspects to the useState hook:

  1. The argument to useState is its initial value. Therefore, with useState(''), we are setting the initial value of userText to a blank string.
  2. The useState function returns an array of two values. The first one is the current value of the variable that is being tracked in the component state. Therefore, in const [userText, setUserText] = useState(''); , userText represents that current value. It becomes a constant that can be used in the component code.
  3. The second value returned from the useState function is a function itself. This function updates the paired state variable. So setUserText updates the userText value.

Within the App component, let’s create a helper method, called updateUserText. With this method, the input element will set the userText in the hook state. How?

This helper will call the new setUserText function, passing in the value from the user’s input. Give updateUserText an event parameter. And that event contains an event.target.value, relevant to the user’s typed input:

const App = () => {
const [userText, setUserText] = useState('');
const updateUserText = event => {
setUserText(event.target.value);
console.log(‘current userText’, userText);
}

We’re going to apply this helper method within the onChange handler of the input field. The onChange handler takes a callback function, which fires whenever the user interacts with the input. This callback has an event parameter (that contains the event.target.value), which will now be passed to updateUserText. Also apply the userText constant as the value for the input element:

<input value={userText} onChange={updateUserText} />

Now start up the application, and check out the app. Fire npm run start in the project’s directory on the command line. The app should open up automatically at localhost:3000. If all goes well, you should see the Type Race title and the input field!

Try typing into the input with the browser’s developer console open. And you should see something very similar to this:

Press enter or click to view image in full size

Cool! The useState hook is definitely working 😁

Comparison to traditional class Component state

Let’s compare the App component function above with the more traditional class component state approach:

There are a couple differences to highlight:

  • On a sheer matter of line number brevity, the functional approach with hook state wins. Using hooks took 14 lines. But the traditional class component took 16.
  • The class approach uses the this object to access this.state, this.setState, and this.updateUserText. It has long been the bane of the React developer to carefully pay attention to how the this object is bound to methods within components. Fixes like .bind(this) are unclean. Using the class property initializer syntax is nice for helper methods. But you still need to remain cognizant of the fact that your method must have access to the this.setState function that gets attached when your component inherits from the React.Component class.

Why is it called a “Hook”?

From the learnings above, you can think of the useState function as a way to hook into the React state functionality. The functional App component, is attaching (hooking), the React concept of state into itself.

In the previous land of extending React.Component, component classes inherited all the React functionality. Now, functional components hook into it.

Complete Type Race

Let’s complete type race. The next goal is to allow the user to select from a few different text snippets. We’ll also time the attempt. Overall, we’ll need to add more state to the App, as well as quite a bit of JSX.

For starters, let’s add the ability for the user to select a snippet to type. Above the existing line to set the state for the userText, let’s add the initial SNIPPETS array, and a line to set the state for a chosen snippet:

const SNIPPETS = [
'Bears, beets, battlestar galactica',
"What's Forrest Gump's password? 1Forrest1",
'Where do programmers like to hangout? The Foo Bar'
];
const [snippet, setSnippet] = useState('');
const [userText, setUserText] = useState('');

Then let’s set up a helper method which will allow the user to choose the snippet. Internally, the helper method will call the returned setState function from the useState hook. Add this above the return line:

const chooseSnippet = snippetIndex => () => {
console.log('setSnippet', snippetIndex);
setSnippet(SNIPPETS[snippetIndex]);
};
return (

Note the double arrow syntax. This sets up chooseSnippet to return a callback function itself. For example, chooseSnippet(0) returns a function itself, that will end up calling setSnippet(SNIPPETS[0]);. This is crucial for the JSX, which will update to display the snippet, and provide buttons that allow the user to set the snippet:

<div>
<h2>Type Race</h2>
<hr />
<h3>Snippet</h3>
{snippet}
<input value={userText} onChange={updateUserText} />
<hr />
{
SNIPPETS.map((SNIPPET, index) => (
<button onClick={chooseSnippet(index)} key={index}>
{SNIPPET.substring(0, 10)}...
</button>
))
}
</div>

A couple key notes:

  • Each button is a substring of its first ten characters of the snippet, with an ellipses attached to the end of it. Notice that the onClick calls chooseSnippet, and passes in the index. This results in a callback function. This returned callback function will internally call setSnippet with the passed index. Why was it coded this way?
  • The onClick handler must reference a function, and not call the function itself. It’s one of the biggest gotchas with React: calling a function within the JSX can trigger state changes that end up re-triggering another render (a render is triggered when component state changes). Therefore, the re-render goes through the JSX, and re-calls the function again. This then triggers a state changes, recalling render — and dang, your app is in an infinite loop 😥
  • Using the index in a map function as the key is an anti-pattern. The proper approach is to refactor and use id’s within entire SNIPPET objects for each item in the SNIPPETS array. But it’s a bit overkill in this learning scenario.

Next, let’s add the gameState. This will be used to both keep track a few things:

  • victory: whether or not the user has completed typing the snippet
  • startTime: when the user has started the game, by choosing a snippet
  • endTime: the time in between starting the game, and finishing the snippet

When the user selects a snippet, this should add a startTime. Therefore, we’ll use setGameState to set the value of gameState.startGame. Note that when you use the setter method returned from the useState hook, you must provide an entirely new object or value. Therefore, we’ll create an object that consists of all the current data in the gameState object, using the spread operator. Then we’ll override the startTime field to new Date().getTime():

  setSnippet(SNIPPETS[snippetIndex]);
setGameState({ ...gameState, startTime: new Date().getTime() });

The other part of the game state contains the victory and the endTime. Both should update in the event that the user’s input matches the snippet. The endTime will be a new Date().getTime minus the set gameState.startTime:

const updateUserText = event => {
setUserText(event.target.value);
if (event.target.value === snippet) {
setGameState({
...gameState,
victory: true,
endTime: new Date().getTime() - gameState.startTime
});
}
}

With these changes, we can now add in relevant JSX for the gameState.victory case:

Back in the application, the result of a win should be something like this:

Press enter or click to view image in full size

😎 Alright, that completes the App for now! 🎊

At this point, feel free to add any css and styling changes you like.

What other hooks are there?

Beyond useState, there are other hooks that can be used to incorporate React concepts into functional React components.

The biggest one after useState, is useEffect. The useEffect hooks tells React that the component needs to fire an effect after a render. The effect is a callback function that you provide. And under the hood, the React engine will fire off that effect when the DOM updates (in other words, after a render).

Components will often have side effects like fetching data, setting up a subscription, or manually changing the DOM (for example, to set the browser focus on an element). These are perfect use cases for the useEffect hook.

This hook can become the place where you put your functionality that had previously lived in lifecycle methods (componentDidMount, componentWillUnmount, etc).

In Type Race, a useEffect hook could be used to set the document.title in the victory case:

import React, { useState, useEffect } from 'react';
useEffect(() => {
if (gameState.victory) document.title = 'Victory!';
});

const updateUserText = event => {

Now when you get to the victory case, you should see the tab’s title change to “Victory!”:

Conclusion

Now that you’ve gone through some of the code with React hooks, hopefully, you’re seeing why this update has galvanized the React community. There has always been a push to create clean functional components. But the drawback was that in order to create local component state, you had no choice but to use the Component class approach.

Now with React hooks, we have the best of both worlds: clean functional components that can use state.

Connect with David

If you enjoyed this content, check out David’s website at https://davidtkatz.com where you can find links to reach out and connect with him.

Also, David is a course creator. He has published 15 courses with over 170,000 students from 192 countries around the world. His latest one teaches React and Redux, in full bootcamp style! Since you’re here from Medium, feel free use the special Medium discount to access the full course:

https://www.udemy.com/react-redux-bootcamp/?couponCode=FROMMEDIUM

--

--

David Katz
David Katz

Written by David Katz

David Katz is a software engineer and course creator, with 16 courses published so far. Check them out at https://davidtkatz.com/

Responses (8)