DEV Community

Christopher Kade
Christopher Kade

Posted on

Introduction to React Hooks

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>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

All it does is allow the user to input something, which is saved in the components internal state and displayed bellow, like so:

render of the component mentioned

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('')
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

Where, on mount, we fetch a picture, save it in the internal state and display it, it looks something like this:

render of the component mentioned

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(() => {
  // ...
})
Enter fullscreen mode Exit fullscreen mode

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(() => {
  // ...
}, [])
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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:

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)

Collapse
 
nhanquach profile image
Nhรขn Quรกch

This article is very on point, super easy to understand.
Thank you!

Collapse
 
viipiin profile image
Viipiin

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

Collapse
 
christopherkade profile image
Christopher Kade

Glad you liked it ๐Ÿ˜ƒ

Feel free to follow me here or on Twitter, I'll share more articles related to React in the future.

Collapse
 
gixxerblade profile image
Steve Clark ๐Ÿคทโ€โ™€๏ธ

Great article! Straight to the point. One issue I ran into was not wrapping useEffect() around another function. One example was a fetch function.

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(url, options);
      const data = await response.json();
      const [item] = data.results;
      setData(item);
      setLoading(false);
    };
    fetchData();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

As you can see I wrapped the useEffect() around the fetchdata() function. The
// eslint-disable-next-line react-hooks/exhaustive-deps comment was a whole other issue.

Collapse
 
jakenherman profile image
Jaken Herman

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.

Collapse
 
iambudi profile image
I am Budi

Nice. As a react beginner this article is helpful in transitioning from class to functional. Thank you.
Please continue writing the other hooks.

Collapse
 
imichaelowolabi profile image
Michael Owolabi

Great article here Christopher. Thanks

Collapse
 
srikanthsd profile image
isrikanthd

Its beginner friendly.
You did it in a clean way.
One of the damn good explanations.

Thanks man!

HappyReact