DEV Community

Cover image for Introduction to React Hooks Part 1
Natalie Smith ๐Ÿณ๏ธโ€๐ŸŒˆ
Natalie Smith ๐Ÿณ๏ธโ€๐ŸŒˆ

Posted on • Edited on

Introduction to React Hooks Part 1

Prerequisites

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 and componentDidUpdate. 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.

enter image description here
(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 and useEffect 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)