React Hooks have been on everyone's mind for a while now, and now that the hype has died down, I thought it would be nice to write a short introduction on them and go through concrete use-cases.
React Hooks have been introduced with React version 16.8, they allow us to use features that were once reserved to class components (such as internal state, lifecycle hooks etc.) in functional components.
This is great, as writing functional components is often preferred by the community as they offer advantages, namely: code that is easier to read and maintain, easier to test and often following better practices. For example, it's easier to create presentational, container and business logic functional components than it is using a class-based components.
Today, we'll only cover two of the most used hooks: useState
and useEffect
.
Make sure you have a grasp of React's fundamentals to take full advantage of this lesson, especially the concept of internal state and lifecycle hooks.
To follow along, feel free to clone the following repository where we'll transform class components into functional components using these two hooks. These components can be found under /components/ExampleUS
and /components/ExampleUE
.
useState
Alright, we have the following class-based component:
class ExampleUS extends React.Component {
state = {
value: ''
}
onChange = event => {
this.setState({
value: event.target.value
})
}
render() {
return (
<article>
<h1>useState example</h1>
<input
type="text"
value={this.state.value}
onChange={this.onChange}
/>
<p>
Value: {this.state.value}
</p>
</article>
)
}
}
All it does is allow the user to input something, which is saved in the components internal state and displayed bellow, like so:
This component requires an internal state, so using a class-based approach made sense before 16.8, but the useState
hook will allow us to transform it into its functional counterpart.
useState syntax
The useState
syntax is very easy to grasp:
const [value, setValue] = useState('')
Where value
is the variable to which we will bind the state, setState
is the method to be called to update it and the parameter passed to useState
is the state's default value. Pretty easy, right?
Transforming the component
Going from a class component to a functional one will take 2 easy steps:
1) First, we change the declaration of the component into a functional one
// Changed the declaration of the component
const ExampleUS = () => {
state = {
value: ''
}
// onChange is now assigned to a constant variable
const onChange = event => {
this.setState({
value: event.target.value
})
}
// Removed the render method,
// Functional components directly return the JSX to be rendered
return (
<article>
<h1>useState example</h1>
<input
type="text"
value={this.state.value}
onChange={this.onChange}
/>
<p>
Value: {this.state.value}
</p>
</article>
)
}
2) Let's now remove all traces of the class' context (this) and state
const ExampleUS = () => {
// Removed the state declaration
// Removed the call to this.setState()
const onChange = event => {}
// Removed all calls to the context
return (
<article>
<h1>useState example</h1>
<input
type="text"
onChange={onChange}
/>
<p>
Value:
</p>
</article>
)
}
The final result
Alright, we can now use useState
with the syntax mentioned before to create an internal state.
Here's what the final component looks like (don't forget to import the hook):
import React, { useState } from "react"
const ExampleUS = () => {
// We declare the state and the method to update it
const [value, setValue] = useState('')
// On input, call setValue with the new state value
const onChange = event => {
setValue(event.target.value)
}
// Bind the input to the state value and display it
return (
<article>
<h1>useState example</h1>
<input
type="text"
value={value}
onChange={onChange}
/>
<p>
Value: {value}
</p>
</article>
)
}
useEffect
For this example, we have the following component:
class ExampleUE extends React.Component {
state = {
url: ''
}
/**
* Fetch a random dog photo and save its URL in our state
*/
componentDidMount() {
fetch("https://dog.ceo/api/breeds/image/random")
.then((res) => res.json())
.then(data => this.setState({
url: data.message
}))
}
render() {
return (
<article>
<h1>useEffect example</h1>
<img src={this.state.url} alt="dog picture"/>
</article>
)
}
}
Where, on mount, we fetch a picture, save it in the internal state and display it, it looks something like this:
The focal point being the lifecycle hook componentDidMount
that is called whenever the component is mounted (meaning whenever it is inserted into the DOM tree). We will use the useEffect
hook to do the exact same thing but in a functional component.
useEffect syntax
Once again, this hook's syntax is easy to understand and use:
useEffect(() => {
// ...
})
It takes as its first parameter a callback that will be triggered each time the component is rendered.
But in our case, we only wish to trigger it once, when the component is mounted, right?
To do so, we can pass useEffect
a second parameter, an array of variables that will trigger the callback only when they are modified (instead of triggering it at every render of the component). We can also pass an empty array ([]
) to tell the callback to be triggered only on mount and dismount of the component, making it look like so:
useEffect(() => {
// ...
}, [])
Transforming the component
We'll skip this part, as it doesn't change much from the previous iteration.
The final result
// Don't forget to import both hooks
import React, { useState, useEffect } from "react"
const ExampleUE = () => {
const [url, setUrl] = useState('')
// On component mount, the callback is called
// Fetch retrieves a picture and saves it in our internal state
// The second parameter tells useEffect
// to only be triggered on mount and dismount
useEffect(() => {
fetch("https://dog.ceo/api/breeds/image/random")
.then((res) => res.json())
.then(data => setUrl(data.message))
}, [])
return (
<article>
<h1>useEffect example</h1>
<img src={url} alt="dog picture" />
</article>
)
}
Wrapping up
React Hooks are a great addition to the library, they provide considerable advantages and make the developer experience much smoother.
One important thing to note is that there are many other hooks, some more used than others and I invite you to read up on the official documentation as it is very well produced.
Other references include:
- Robin Wieruch's "How to fetch data with React Hooks?"
- Matthieu Lux's "React Hooks, my introduction"
Thank you for reading, if you've learned something feel free to follow me on Twitter @christo_kade as I'll share all my new blog posts about React, Vue and the JS ecosystem as a whole β€οΈ
Top comments (8)
This article is very on point, super easy to understand.
Thank you!
I like the way you describe Hooks in React.
Obviously I'm new to React being a SharePoint dev but It is going to help a lot in coming days.Let's build connection with you so that I can learn from you
HappyReact
Glad you liked it π
Feel free to follow me here or on Twitter, I'll share more articles related to React in the future.
Great article! Straight to the point. One issue I ran into was not wrapping
useEffect()
around another function. One example was afetch
function.As you can see I wrapped the
useEffect()
around thefetchdata()
function. The// eslint-disable-next-line react-hooks/exhaustive-deps
comment was a whole other issue.Nice article! Thanks for sharing. I've been avoiding hooks for some reason, mainly because most articles regarding hooks go into way too much detail - this was great because it was straight to the point.
Nice. As a react beginner this article is helpful in transitioning from class to functional. Thank you.
Please continue writing the other hooks.
Great article here Christopher. Thanks
Its beginner friendly.
You did it in a clean way.
One of the damn good explanations.
Thanks man!
HappyReact