Prerequisites
- An understanding of the main concepts of the React library
- JavaScript fundamentals (https://www.robinwieruch.de/javascript-fundamentals-react-requirements)
What are React Hooks?
React Hooks allows us to "hook into" React's features like local state-management or lifecycle methods with function components instead of class components.
What problems does it solve?
Okay, so if class components can do the same things that Hooks aims to do - setting state, lifecycle methods, context, etc.. then why switch?
Large class components can be cumbersome
As our application grows in size, it becomes more and more involved in stateful logic and side effects. Often times, it may contain the same logic spread across lifecycle methods or it may contain some unrelated logic.
I think the React docs describe it best by saying:
For example, components might perform some data fetching in
componentDidMount
andcomponentDidUpdate
. However, the same
componentDidMount
method might also contain some unrelated logic
that sets up event listeners, with cleanup performed in
componentWillUnmount
. Mutually related code that changes together
gets split apart, but completely unrelated code ends up combined in a
single method. This makes it too easy to introduce bugs and
inconsistencies.In many cases itβs not possible to break these components into smaller
ones because the stateful logic is all over the place. Itβs also
difficult to test them. This is one of the reasons many people prefer
to combine React with a separate state management library. However,
that often introduces too much abstraction, requires you to jump
between different files and makes reusing components more difficult.
So Hooks allows us to split components into smaller functions based on what pieces are needed like fetching data.
Believe it or not, classes are confusing
Let's say you are just starting out and heard about this amazing library called React. Alright, you decide to jump on the bandwagon and learn about it but you stumble upon the dreaded this
keyword and it's just downright confusing, well, at least it was to me. Then, what about the constructor(props) {super(props)}
and the fact you have to remember to bind event handlers.
Hooks let you use more of Reactβs features without classes
Hooks embrace functions, but without sacrificing the practical spirit of React. Hooks provide access to imperative escape hatches and donβt require you to learn complex functional or reactive programming techniques.
Reuse of stateful logic
Remember render props or higher-order components, it was a way to share the same functionality across multiple components. However, you have to restructure them as you use them, it becomes quite complicated and your code becomes harder to follow as you progress. So this will also cause "wrapper hell" or when your application has a bunch of nested components.
(https://twitter.com/GrexQL/status/1045110734550589441?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1045110734550589441&ref_url=https%3A%2F%2Fwww.polidea.com%2Fblog%2Freact-hooks-vs-wrapper-hell-writing-state-in-a-function-with-ease%2F)
Hooks allows you to reuse stateful logic without changing your component hierarchy
Setting State - Using the State Hook
This will show you how to setState and update the state in react.
First let's import it
import React, { useState } from 'react'
Let's declare a state variable
We're going to have a side by side comparison with Hooks and Class components.
Hooks
const App = () => {
const [randomNum, setRandomNum] = useState(0);
}
Classes
class App extends Component {
constructor(props) {
super(props);
this.state = {randomNum: 0}
}
render() {
. . .
}
}
But wait, why is there randomNum and setRandomNum? The first one, randomNum would be your state variable while setRandomNum would be a function that updates your state - randomNum.
Putting set
in front of your updater function is a naming convention and you will likely see this everywhere Hooks is used.
So, let's break it down:
const [yourStateName, setYourFunctionUpdaterName] = useState(yourDefaultState);
In our example, I declared a state variable called randomNum and my updater function setRandomNum. I then, gave it a default state of 0.
How do we read the state?
In a class component, it would look something like this:
<h1>{this.state.randomNum}</h1>
In Hooks, it would look something like this:
<h1>{randomNum}</h1>
Since in function components, we don't have this
we can't call it like before with this.state
. Instead, with Hooks, since it's a variable, we just call it like a variable - {randomNum}
.
Okay, how do we update the state?
In a class component, we would have to do something like this:
this.setState({randomNum: newState})
In Hooks, we would do somthing like this:
setRandomNum(newState)
Okay, let's see this in use
Here's an example of setting state in a class component, here we are generating a random number every time the user clicks our button.
<button onClick={() => this.setState({randomNum: Math.floor(Math.random() * 100) + 1})}>Change</button>
Let's recreate this in Hooks
<button onClick={() => setRandomNum(Math.floor(Math.random() * 100) + 1)}>Change</button>
With, hooks, since it's a function we just call it like a function. Of course we don't specify what it needs to update -> setRandomNum
because we already inistalized the state attached to the updater function -> const [randomNum, setRandomNum] = useState(0)
And of course, you can have multiple states with different values just like classes:
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
Using the useEffect hook
The effect hook allows us to perform side effects in function components, so fetching data from an API, setting up a subscription, or changing the DOM in React components are some use cases for this.
Also, useEffect can also be used as a combination of componentDidMount
, componentDidUpdate
, and componentWillUnmount
So let's take a look at how we go about fetching data in a class component:
componentDidMount() {
fetch('https://hn.algolia.com/api/v1/search?query=redux')
.then(response => response.json())
.then(result => this.setState({data: result.hits});
}
Now, let's take a look at data fetching in Hooks:
First let's import it
import React, {useState, useEffect} from 'react';
Now let's see it in action
const App = () => {
const [hits, setHits] = useState([])
useEffect(() => {
fetch('https://hn.algolia.com/api/v1/search?query=redux')
.then(response => response.json())
.then(result => setHits(result.hits));
});
. . .
}
By using this Hook, you tell React that your component needs to do something after render. You will pass it a function -> useEffect( () => {})
and it will call it later after performing the DOM updates.
It's called inside a component because it enables us to access state variables or any props.
It also runs after each render so by default it runs after the first render and after every update so that brings us onto our next topic.
If you take a look at our code in this example, it fetches the data and sets the state but if you actually try to run this you will find yourself in a terrible loop.
But why?
Remember when I said that it runs after every update? Well, when we set the state when we get the result that causes it to update and therefore the hook runs again and again and again.
And how do I fix this?
We only want to fetch data when the component mounts, so we have to provide an empty array as the second arguement to the effect hook to avoid activating it on component updates.
const App = () => {
const [hits, setHits] = useState([])
useEffect(() => {
fetch('https://hn.algolia.com/api/v1/search?query=redux')
.then(response => response.json())
.then(result => setHits(result.hits));
}, [] <-- provide an empty array);
. . .
}
So now if the variable hits
changes then the hook will run again. If the array with the variables is empty, the hook doesn't run when updating the component at all, because it doesn't have to watch any variables.
Alright, so there is a lot of other stuff to cover like Custom Hooks, Context, and much more but that should come along in part 2. So stay tuned!
Before you go, keep in mind these rules π
Important Rules for Hooks
Only Call Hooks at the Top Level
Meaning, don't call them in loops, conditionals, or nested functions
By following this rule, you ensure:
- That they are called in the same order each time a component renders
- It allows React to correctly preserve the state of Hooks between multiple
useState
anduseEffect
calls
Only Call Hooks from React Functions
Donβt call Hooks from regular JavaScript functions
But you can:
- Call Hooks from React function components.
- Call Hooks from custom Hooks ( This will be covered in Part 2)
ESLint Plugin for enforcing these rules
How to install
npm install eslint-plugin-react-hooks --save-dev
// Your ESLint configuration
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
"react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
}
}
If you're using Create React App, this is already included! π₯³
Cover Image: https://miro.medium.com/max/3000/1*Ra-gkqfPqbWVhgP3tR-0Cg.png
Top comments (0)