DEV Community

Samuel Grasse-Haroldsen
Samuel Grasse-Haroldsen

Posted on

useState and useEffect

Today I wanted to write about two of the most common hooks out there. They are provided by the React API, and they are the reason functional components can finally compete with class components. If you want to read more about the reasoning behind hooks check out this article.

useState

useState is how we can access state in our functional components. With class components this is what our state code might look like:

import React, { Component } from 'react';
export default class Card extends Component {

  constructor() {
    super()
    this.state = {
      isToggleOn: true
    };
  }

  handleClick = () => {
    this.setState(state => (
      {
        isToggleOn: !state.isToggleOn
      }
    ));
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        {this.state.isToggleOn ? <div className='card'>{this.props.front}</div> : <div className='card clicked'>{this.props.back}</div>}
      </div>
    )
  }
}

Enter fullscreen mode Exit fullscreen mode

As you can see we need quite a bit of code to set up a simple toggle function that will flip a card when our div is clicked. This React code definitely simplifies the process as opposed to vanilla JS, but we can do better with hooks:

import { useState } from 'react';

export default function Cardd({ front, back }) {

    const [toggle, setToggle] = useState({ isOn: false });

    return(
        <div onClick={ setToggle({ isOn: !toggle.isOn})}>
            {toggle.isOn ? <div className='card'>{front}</div> : <div className='card clicked'>{back}</div>}
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

With hooks we do not need the superfluous this.state or this.props or even this.setState. We can avoid all of that by using the useState hook. We import it, set our state variable name, the function we will use to set that state variables, and then we call useState with our initial state. Make note that when setting the state's variable name and update state function name we do so using an array ex) const [stateName, setStateName] = useState(0);

useEffects

This hook is not quite as straightforward as useState. Because one of the problems the React devs wanted to solve when creating these hooks was minimizing code being split between different life cycle methods, they decided to make an almighty hook that combined all the logic of the life cycle methods into a single hook. Here's a brief summary of useEffect:

  1. useEffect takes two arguments (the second being optional)
  2. the first argument is a function that will run after render and on each update
  3. we can pass an array as a second argument to useEffect that contains dependencies for useEffect to run (if the dependencies haven't changed useEffect will not execute the first argument function)
  4. in our first argument function we can return an optional cleanup function that will execute when our component unmounts

Let's dive right into a simple example.

import React from 'react';
const { useEffect, useState } = React;
export default function Timer() {
  const [seconds, setSeconds] = useState(0);
  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(seconds + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, [seconds]);

  return (
    <h2>{seconds}</h2>
  );
};
Enter fullscreen mode Exit fullscreen mode

Here we are using state and setting up a timer that updates the seconds state every second. We use the setInterval function which executes code continuously with a delay in-between (1000ms). We will need to stop this timer once we no longer need it (clearInterval). With useEffect we can pass an arrow function in with our setInterval inside the body and return clearInterval. This will achieve the same thing as componentDidMount and componentWillUnmount. In our example we are also passing an array to useEffect as the second argument containing the state variable seconds. This tells useEffect to only run it's function if seconds has changed (similar to shouldComponentUpdate).

Fetching Data?

You might be wondering how we fetch data after our component has been rendered. The answer is simple. In our useEffect hook we fetch our data and supply an empty array [] as our dependency. This guarantees that our data will be fetched only once, after rendering.

 useEffect(() => {
    fetch('https://www.dnd5eapi.co/api/spells/')
    .then(r => r.json())
    .then(data => {
      console.log(data);
      setSpells(data.results);
    }
  )}, [])
Enter fullscreen mode Exit fullscreen mode

Top comments (0)