What is a Hook? A Hook is a special function that lets you "hook into" React features. For example, useState
is a Hook that lets you add React state to function components.
When would I use a Hook? If you write a function component and realize you need to add some state to it, previously you had to convert it to a class. Now you can use a Hook inside the existing function component.
Function components in React look like this:
const Example = (props) => {
// You can use Hooks here!
return <div />;
}
or this:
function Example(props) {
// You can use Hooks here!
return <div />;
}
Hooks don’t work inside classes. But you can use them instead of writing classes.
In a React class, we initialize the count
state to 0
by setting this.state
to { count: 0 }
in the constructor:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
In a function component, we have no this
, so we can't assign or read this.state
. Instead, we call the useState
Hook directly inside our component:
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
What does calling useState
do?
- It declares a "state variable".
useState
is a new way to use the exact same capabilities thatthis.state
provides in a class.- Normally, variables "disappear" when the function exits but state variables are preserved by React.
What do we pass to useState
as an argument?
- The only argument to the
useState()
Hook is the initial state. - Unlike with classes, the state doesn't have to be an object. We can keep a number or a string if that's all we need.
- In the example above, we just want a number for how many times the user clicked, so pass 0 as initial state for our variable.
- If we wanted to store two different values in state, we would call
useState()
twice.
What does useState
return?
- It returns a pair of values: the current state and a function that updates it.
- This is why we write
const [count, setCount] = useState()
. This is similar tothis.state.count
andthis.setState
in a class, except you get them in a pair.
Now that we know what the useState
Hook does, our example should make more sense:
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
We declare a state variable called count
, and set it to 0
. React will remember its current value between re-renders, and provide the most recent one to our function. If we want to update the current count, we can call setCount
.
When we want to display the current count in a class, we read this.state.count
:
<p>You clicked {this.state.count} times</p>
In a function, we can use count directly:
<p>You clicked {count} times</p>
In a class, we need to call this.setState() to update the count state:
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
In a function, we already have setCount and count as variables so we don’t need this:
<button onClick={() => setCount(count + 1)}>
Click me
</button>
Let's now review what we learned line by line and check our understanding:
1: import { useState } from 'react';
2:
3: function Example() {
4: const [count, setCount] = useState(0);
5:
6: return (
7: <div>
8: <p>You clicked {count} times</p>
9: <button onClick={() => setCount(count + 1)}>
10: Click me
11: </button>
12: </div>
13: );
14: }
- Line 1: We import the
useState
Hook from React (It lets us keep local state in a function component). - Line 4: Inside the Example component:
- we declare a new state variable by calling the
useState
Hook. It returns a pair of values, to which we give names. - We're calling our variable
count
because it holds the number of button clicks. - We initialize it to zero by passing
0
as the onlyuseState
argument. The second returned item is itself a function. It lets us update the count so we'll name it setCount.
- we declare a new state variable by calling the
- Line 9: When the user clicks, we call
setCount
with a new value. React will then re-render the Example component, passing the new count value to it.
You might have noticed the square brackets when we declare a state variable:
const [fruit, setFruit] = useState('orange');
This JavaScript syntax is called "array destructuring
". It means that we're making two new variables fruit
and setFruit
, where fruit
is set to the first value returned by useState
, and setFruit
is the second. It is equivalent to this code:
var fruitStateVariable = useState('banana'); // Returns a pair
var fruit = fruitStateVariable[0]; // First item in a pair
var setFruit = fruitStateVariable[1]; // Second item in a pair
When we declare a state variable with useState
, it returns a pair of an array with two items. The first item is the current value, and the second is a function that lets us update it. Using [0]
and [1]
to access them is a bit confusing because they have a specific meaning. This is why we use array destructuring instead.
Declaring state variables as a pair of [something, setSomething]
is also handy because it lets us give different names to different state variables if we want to use more than one:
function ExampleWithManyStates() {
// Declare multiple state variables
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('orange');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
In the above component, we have age, fruit, and todos as local variables, and we can update them individually:
function handleOrangeClick() {
// Similar to this.setState({ fruit: 'banana' })
setFruit('banana');
}