Table of Contents
Introduction
Hooks
are a new feature introduced in React16.8.
I’ll try to explain what they are in the following order.
- What are Hooks?
- Rules of Hooks
- State Hook
- Effect Hook
- Custom Hook
What are Hooks?
According to the official React documentation,
Hooks let you use state and other React features without writing a class.
Yup. That’s exactly it!.
Now you can use some React features such as state
in functional components thanks to hooks
!!
I’ll introduce 3 following hooks in this post.
- (1)State Hook: It lets you use
state
andsetState
in functional components. - (2)Effect Hook: It lets you perform side effects such as data fetching in functional components.
- (3)Custom Hooks: Building your custom hooks lets you extract component logic into reusable functions.
Rules of Hooks
There are 2 ground rules you have to follow to use hooks safely.
(1) Only Call Hooks at the Top Level!! Don’t call Hooks inside loops, conditions, or nested functions.
(2) Only Call Hooks from React Functions!! Don’t call Hooks from regular JavaScript functions.
State Hook
You can use state
and setState
in functional components by using a hook called useState
.
Let’s see how to use useState
through comparing a functional component
using useState
to the equivalent class component
.
Equivalent Class Component
It does the following 2 things.
- (1) Define the
count
state and its initialState0
. - (2) Add 1 to
count
each time setState is called.
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
}
}
render() {
return (
<div>
<p>You clicked{this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click Me
</button>
</div>
)
}
}
Functional Component with State Hook
Now it’s time to create a functional component using useState
which does the same thing as the class component above.
The basic syntax of useState
looks like this!
const [state, setState] = useState(initialState)
It’s like you define the state
, setState
and initialState
all together.
If you want to define the same state as the one in the class component above, it would look like this.
import React, { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
}
Notice 3 things in the code above!
- (1)
count
is the equivalent ofthis.state={count:0}
in the class component. - (2)
setCount
is the equivalent ofsetState
part in the class component. - (3)
0
is the initial state ofcount
.
While you are writing up the rest of the code, keep these 2 things in mind.
- (1) You can use
count
directory!(no need to dothis.count
.) - (2) You can update the state by using
setCount
.
import React, { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
// (1) You can use count directory!
<p>You clicked {count} times</p>
// (2) You can update the state by using setCount.
<button onClick={() => setCount(count + 1)}> Click me</button>
</div>
)
}
export default Counter
As a side note, you can define multiple states
like the code below.
import React, { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
const [name, setName] = useState('')
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}> Click me</button>
<p>My name is {name}.</p>
<button onClick={() => setName('テスト太郎')}>Show my name</button>
</div>
)
}
export default Counter
Effect Hook
You can perform side effects in functional components by using a hook called useEffect
!
Let’s see how to use useEffec
through comparing a functional component
using useEffect
to the equivalent class component
.
Example Class Component
In class components, we perform side effects such as fetching data and changing the DOM in componentDidMount
componentDidUpdate
.
Here, it outputs It did mount
in the console after a component is mounted and outputs It did get updated
after updating occurs.
import React from 'react'
class Effect extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
}
}
componentDidMount() {
console.log('It did mount.')
}
componentDidUpdate() {
console.log('It did get updated.')
}
render() {
return (
<div>
<h1>You clicked {this.state.count} times</h1>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
)
}
}
Example Functional Component using useEffect
The useEffect
hook is like a combination of componentDidMount
, componentDidUpdate
and componentWillUnmount
.
It runs after every render including the first render.
When you are building react applications with hooks
this is where you perform side effects.
import React, { useState, useEffect } from 'react'
function Effect() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log('It got rendered')
})
return (
<div>
<h1>You clicked {count} times</h1>
<button onClick={() => setCount(count + 1)}> Click me</button>
</div>
)
}
Custom Hooks
As I mentioned above, building your custom hooks lets you extract component logic into reusable functions.
Let’s suppose there are two components like below.
- (1) A component called
Status
which returnsLogged in
if it receivesid = 1
. - (2) A component called
Message
which returnsWelocme Back
if it receivesid = 1
.
export default function Status(props) {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const handleStateChange = id => {
if (id === 1) {
setIsLoggedIn(true)
} else {
setIsLoggedIn(false)
}
}
useEffect(() => {
handleStateChange(props.user.id)
})
const status = isLoggedIn ? 'Logged in' : 'Sign up'
return (
<>
<h1>Status: {status}</h1>
</>
)
}
export default function Message(props) {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const handleStateChange = id => {
if (id === 1) {
setIsLoggedIn(true)
} else {
setIsLoggedIn(false)
}
}
useEffect(() => {
handleStateChange(props.user.id)
})
const message = isLoggedIn ? 'Welcome Back' : 'Who are you??'
return (
<>
<h1>Message: {message}</h1>
</>
)
}
As you probably noticed, it’s very redundant.
You can build a custom hook
to extract the same logic exists in both components into one reusable function.
※It is very important that you name your custom hook starting with use
.
In this case, I named my custom hook useLogIn
.
import { useState, useEffect } from 'react'
export default function useLogIn(userId) {
const [isLoggedIn, setIsLoggedIn] = useState(false)
// The login you want to reuse.
const handleStateChange = id => {
if (id === 1) {
setIsLoggedIn(true)
} else {
setIsLoggedIn(false)
}
}
// Perform side effects in useEffect.
useEffect(() => {
handleStateChange(userId)
})
return isLoggedIn
}
Using useLogIn
, we can simplify Status
and Message
components.
import React from 'react'
import useLogIn from './useLogIn'
export default function Status(props) {
const status = useLogIn(props.user.id) ? 'Logged in' : 'Sign up'
return (
<>
<h1>Status: {status}</h1>
</>
)
}
import React from 'react'
import useLogIn from './useLogIn'
export default function Message(props) {
const message = useLogIn(props.user.id) ? 'Welcome Back' : 'Who are you??'
return (
<>
<h1>Message: {message}</h1>
</>
)
}
The custom hooks
can be used for other cool stuff, check out the official document about Building Your Own Hooks.