Hooks have been officially added to React as of React 16.8. One of the main benefits of Hooks in React is that you can use state and effect (which is generally a combination of different life cycles into one) in a component without creating a class.
In this tutorial, you’ll learn how to exactly use hooks in React. We will build a simple website that gives the user some options to pick from in order to generate some random number trivia.
You can check out the demo for the website here, and the source code on the GitHub repository.
Let’s first start by creating a react app. Since our website will be very simple, we will use the create-react-app
command. If you don’t have it installed, you can install it using npm
.
Run the following in your terminal or CMD:
npm i -g create-react-app
This will install create-react-app
globally.
Now we can create our React app:
create-react-app numbers-trivia
Running this command will create a directory in the working directory with the name you supply for the React app. I named it numbers-trivia but you can call it whatever you want.
Inside that directory, it will also install all the packages and files needed to run the website. It will install packages like react, react-dom, react-scripts and more.
Once it’s done, change into the newly created directory and start the server:
cd numbers-trivia
npm start
Once you start the server, a web page of your website in your favorite browser will open. You will see a page with just the logo and a link to learn React.
Before we start changing the page, if you are not familiar with React, let’s take a look at the structure of our React app. It should look something like this:
In our root directory, we will find a bunch of directories. node_modules holds all the packages React needs to run and any package you might add. public holds the files our server serves.
The directory that we will spend most of our time in is the src directory. This directory will hold all of our React components.
As you can see there are a bunch of files in that directory. index.js is basically the file that renders our highest React component.
We only have one React component right now which is in App.js. If you open it, you will find the code that renders the logo with the link to learn React that you currently see on the website.
So, in order to see how changing our App component will change the content of the website, let’s modify the code by removing the current content and replacing it with our favorite phrase: “Hello, World!”
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<h1>Hello, World!</h1>
</header>
</div>
)
}
}
export default App
We just replaced the content of the header element. Now, if your server is still running you will see that your page updated without you refreshing it, and you will see that the previous content is replaced with Hello, World!
So now we know how and where we should edit our React components in order to get the result we want. We can go ahead and start with our objective.
What we’ll do for this website is the following:
- Show a welcome message the first time the user opens the website, then replace it with a message prompting the user to try the generator.
- Render a form with text and select inputs. The text input is where the user can enter the number they want to see trivia about, and the select input will provide the user with options related to the trivia.
- On submitting the form, send a request to this API to fetch the trivia we need.
- Render the trivia for the user to see it.
Let’s start by organizing our directory structure first. In React it’s good practice to create a directory inside src holding all the components. We’ll create a directory called components. Once you create the directory, move App.js into there. We will also create a directory called styles and move App.css and index.css into it.
When you do that, you will need to change the imports in your files as following:
- in index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import '../styles/index.css';
import App from './components/App';
import as serviceWorker from './serviceWorker';
2. in App.js:
import React, { Component } from 'react';
import logo from './logo.svg';
import '../styles/App.css';
Our directory structure should look like this now:
We will go ahead now and start building our webpage.
The first thing in our objectives list is showing a welcome message when the user first opens the webpage. It will show up for 3 seconds, and then changes to another message that will prompt the user to try out the trivia generator.
Without hooks, this could be done by using React’s lifecycle method componentDidMount
which runs right after the component first renders.
Now, we can use the effect hook instead. It will look something like this:
useEffect(() => {
//perform something post render
});
The function you pass to useEffect
will be executed after every render. This combines the lifecycles methods componentDidMount
and componentDidUpdate
into one.
What if you want to do something just after the first time the component renders, like in componentDidMount
? You can do this by passing a second parameter to useEffect
.
useEffect
accepts an array as a second parameter. This parameter is used as a condition on when to perform the passed function. So, let’s say you want to change a counter only after the variable counter changes, you can do it like so:
useEffect(() => document.title = `You have clicked ${counter} times`, [counter]);
This way, the function passed to useEffect
will run after render only if the value for counter
changes.
If we want the function to run only after the first render, we can do that by passing an empty array as the second parameter.
Let’s come back now to what we want to do. We want to show a welcome message when the user first opens the page, then change that message after 3 seconds. We can do that by adding the following inside App.js:
import React, {useEffect} from 'react';
import Form from './Form';
import '../styles/App.css';
function App() {
useEffect(() => {
setTimeout(() => {
let welcomeMessage = document.getElementById("welcomeMessage");
welcomeMessage.innerHTML = "Try Out Our Trivia Generator!";
}, 3000);
}, []);
return (
<div className="App">
<header className="App-header">
<h1 id="welcomeMessage">Welcome to Numbers Trivia!</h1>
<Form />
</header>
</div>
); }
export default App;
Here’s what we’re doing:
- Line 1: We added an import for
useEffect
- Line 4: We changed our class component into a function component
- Line 5–10: we added an effect to our function component. This effect sets a timer after 3 seconds that will change the text in the element with the id
welcomeMessage
. Because we passed an empty array touseEffect
, this effect will only run once. - Line 11–17: We replaced the previous code in App.js to render an h1 element having the id
welcomeMessage
, which is our target element.
Okay, now go to our web page and see the changes. At first, the welcome message “Welcome to Numbers Trivia!” will show up, then 3 seconds later it will change into “Try Out Our Trivia Generator!” We just used a React Hook!
Next, let’s create our form input elements. To do that, we will create a new React component called Form
. Create in the directory components the file Form.js. For now, it will just include the following:
import React from 'react';
function Form(props){
}
export default Form;
This will create the new React component. We’re just importing React, then we’re creating a function called Form. As we said earlier in the tutorial, with the use of hooks we can now create components as stateful functions rather than classes. And in the last line, we’re exporting Form in order to import it in other files.
In the form, we will have a text input and select elements. This is based on the API we’re using. In the API, two parameters can be sent:
- number: the number you want to get the trivia for. It can be an integer, a date of the form month/day, or the keyword random which will retrieve facts about a random number.
- type: the type of information you want to get. There are a few types: math, date, year, or, the default, trivia.
We will consider the text input element as optional. If the user does not enter a number or a date, we will send the keyword random for the number element.
Let’s add the following code inside the Form function in order to render our form:
function Form(props){
return (<form>
<div>
<input type="text" name="number" placeholder="Enter a number (Optional)" />
</div>
<div>
<select name="type">
<option value="trivia">Trivia</option>
<option value="math">Math</option>
<option value="date">Date</option>
<option value="year">Year</option>
</select>
</div>
<button type="submit">Generate</button>
</form>);
}
This will create the form with the text input and select and button elements.
After that, we need to import and render the Form component in our App component:
import React, {useEffect} from 'react';
import Form from './Form';
import '../styles/App.css';
function App() {
useEffect(() => {
setTimeout(() => {
let welcomeMessage = document.getElementById("welcomeMessage");
welcomeMessage.innerHTML = "Try Out Our Trivia Generator!";
}, 3000);
}, []);
return (
<div className="App">
<header className="App-header">
<h1 id="welcomeMessage">Welcome to Numbers Trivia!</h1>
<Form />
</header>
</div>
);
}
export default App;
We have changed the imports to import our Form
component, and we added <Form />
to render the form.
Let’s also add more styles just to make our form look a little better. Add the following at the end of App.css:
form {
font-size: 15px;
}
form input, form select {
padding: 5px;
}
form select {
width: 100%;
}
form div {
margin-bottom: 8px;
}
If you refresh the page now, you will find it has changed and now it’s showing our form.
Now, we need to add some logic to our form. On Submit, we need to get the values of our input elements, then call the API to retrieve the results.
To handle form elements and their values, you need to use the state of the component. You make the value of the element equal to a property in the state of the component.
Before hooks, in order to get the value in the state you would have to use this.state.value
, and then to set the state, you will need to call this.setState
.
Now, you can use the state hook. The state hook is the useState
function. It accepts one argument, which is the initial value, and it returns a pair of values: the current state and a function that updates it. Then, you will be able to access the current state using the first returned value, and update it using the second returned value which is the function.
Here’s an example:
const [count, setCount] = useState(0);
in this example, we call the useState
hook and pass it 0, and we set the returned value equal to count
and setCount
. This means that we have created state variable called count
. Its initial value is 0, and to change its value we can use setCount
.
For our Form
component, we need two state variables, one for the text input which we will call number
, and one for the select input which we will call type
. Then, on change event for these two input elements, we will change the values for number
and type
using the function returned by setState
.
Open our Form
component and change it to the following:
import React, { useState } from 'react';
function Form(props){
let [number, setNumber] = useState("random");
let [type, setType] = useState("trivia");
function onNumberChanged(e){
let value = e.target.value.trim();
if(!value.length){
setNumber("random"); //default value
} else {
setNumber(value);
}
}
function onTypeChanged(e){
let value = e.target.value.trim();
if(!value.length){
setType("trivia"); //default value
} else {
setType(value);
}
}
return (<form>
<div>
<input type="text" name="number" placeholder="Enter a number (Optional)" value={number} onChange={onNumberChanged} />
</div>
<div>
<select name="type" value={type} onChange={onTypeChanged}>
<option value="trivia">Trivia</option>
<option value="math">Math</option>
<option value="date">Date</option>
<option value="year">Year</option>
</select>
</div>
<button type="submit">Generate</button>
</form>);
}
export default Form;
- Line 1: add an import for
useState
hook. - Line 3–4: create two state variables
number
andtype
usinguseState
. Here we pass random as the initial value for number, and trivia as initial value for type because they are the default values for the parameters in the API. - Line 5–10: implement input change handler functions for both text and select inputs, where we change the value of the state variables using the functions returned by
useState
. If the value is unset, we automatically change the values to the default value. - Line 13: pass the
onNumberChanged
function toonChange
event for text input. - Line 16: pass the
onTypeChanged
function toonChange
event for select input.
In addition, let’s go back to our App
component to modify it and use states. Instead of modifying our welcome message by changing the innerHTML
of the element, we will use a state. Our App component should now be like this:
import React, {useEffect, useState} from 'react';
import Form from './Form';
import '../styles/App.css';
function App() {
const [ welcomeMessage, setWelcomeMessage ] = useState(
"Welcome to Numbers Trivia!",
);
useEffect(() => {
setTimeout(() => {
setWelcomeMessage("Try Out Our Trivia Generator!");
}, 3000);
}, []);
return (
<div className="App">
<header className="App-header">
<h1>{welcomeMessage}</h1>
</header>
<Form/>
</div>
);
}
export default App;
Now, we are using useState
to declare and initialize our welcome message. It will return welcomeMessage
, our state variable, and setWelcomeMessage
, which we will use to change the value of welcomeMessage
after 3 seconds from “Welcome to Numbers Trivia!” to “Try Out Our Trivia Generator!”
What’s left now is to add a function to handle the form’s onSubmit
event. On submit, we will send a request to the API with our parameters, then display the result.
In order to perform the request, we need to install axios:
npm i axios
Then, require axios at the beginning of Form.js:
const axios = require('axios');
Now, add the following function below onTypeChanged
in our Form component:
function onSubmit(e){
e.preventDefault();
axios.get('http://numbersapi.com/' + number + '/' + type)
.then(function(response){
let elm = document.getElementById('result');
elm.innerHTML = response.data;
}).catch(function(e){
console.log("error", e); //simple error handling
});
}
We’re just performing a request to the API, passing the number
and type
then displaying the result in an element of id result
(which we will add in a minute). In case of an error, we’re just displaying it in the console just as a simple error handling.
Now, let’s add this function as the handler for the form onSubmit
event in the return statement in Form.js:
<form onSubmit={onSubmit}>
The only thing left is to add the #result
element. We can add it in App.js before <Form />
:
<div id="result" style={{marginBottom: '15px'}}></div>
Alright, now go to your website and discover all new trivia about numbers!
Conclusion
You just created a React app and used some React Hooks! Of course, there’s still more to explore and learn, so be sure to head to the docs to learn more about React Hooks.