React useState and useEffect Hooks for beginners

Introduction

React is a popular JavaScript library known for its simplicity and state management capabilities. It helps developers create user interfaces using reusable components and offers a more efficient way to update web pages, making apps faster and smoother.

Managing state in React can be a challenging task but React Hooks come to the rescue by simplifying state management making your code cleaner and more efficient. In this beginner-friendly article, we'll be exploring two essential React Hooks: useState and useEffect and how to use them for effective state management in your React applications.

Since, React Hooks are said to simplify state management, we will be starting by understanding state and its connection to hooks.

What is State in React?

State is a fundamental concept that allows you to store and manage data within your components, it serves as an information holder. It's how an application remembers and reflects what's happening in real time, such as user interactions, form input, etc. For instance, in a messaging app, the state stores the ongoing conversation, user profiles, and message history. As the state changes, the app adjusts its display accordingly, ensuring a seamless and responsive user experience. Now, that we have established how state works, let's talk about components.

What are components?

Components are one of the core concepts of React. They are the foundation upon which you build user interfaces

Components in React are like the different parts that make up a user interface on a website or app. You can think of them as pieces in a jigsaw puzzle. Each component has a specific job and determines how a part of the website should look and work. For example, a button, a navigation bar, a form, or even an entire page can be considered as components. They are the building blocks that, when combined, create the entire user interface. So, in simpler terms, components are like the individual parts of a website or app that come together to make everything work and look good.

There are two types of components in React: class and functional components. These components play a significant role in building user interfaces. I've mentioned the importance of state in managing data within components. To manage this data, React class components use complex patterns and methods but functional components make state management more straightforward due to the presence of hooks.

Below is an example of a React class component in action:


import { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

export default Counter;

The code above defines a React class component;Counter, which displays a number (the count) and a button. When the button is clicked, the count increases by one. It uses React's state management to keep track of the count and update the display.

Below is an example of a React Functional component in action:


import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default Counter;

The code above shows a simple React functional component Counter, using the useState hook to manage and display a count. When the "Increment" button is clicked, it increases the count, and the user interface updates to reflect the new count.

Now that we have seen Examples of both React class and functional components achieving the same goal and observed the smoother development experience in functional components largely due to the presence of the useState React hook, let me explain react hooks.

What are React Hooks?

React Hooks help manage state across different components without the need for class components. They are a set of functions provided by React that allow functional components to manage and manipulate their internal state. React Hooks, such as useState, useEffect, and useContext etc provide a more concise and organized way to handle component logic, making it easier to build and maintain React applications.

Introducing useState

The useState Hook is a simple function that takes an initial state as its argument and returns an array with two elements: the current state value and a function to update it. With useState , you have the ability to declare and control variables that store kinds of data like numbers, strings, objects, or arrays. Additionally, whenever there is a change in the state, the component will automatically refresh itself.

Let's see how useState works in the code below:

import { useState } from 'react';

function NameForm() {
  const [name, setName] = useState(''); 

  const handleNameChange = (e) => {
    setName(e.target.value);
  };

  const clearName = () => {
    setName('');
  };

  return (
    <div>
      <h1>Simple Name Form</h1>
      <input
        type="text"
        placeholder="Enter your name"
        value={name}
        onChange={handleNameChange}
      />
      <p>Hello, {name ? name : 'Stranger'}!</p>
      <button onClick={clearName}>Clear Name</button>
    </div>
  );
}

export default NameForm;

Here's a breakdown of how useState is used in the code above:

Managing State:

const [name, setName] = useState('');

The useState hook is called with an initial state value of an empty string ''.It returns an array with two elements:

  • name: This is the current state value, initialized with an empty string.

  • setName: This is the function used to update the name state.

    Event Handling:

  • Under this, we have the Handling input changes and clearing name event handlers.

    Handling Input Changes:

      const handleNameChange = (e) => {
        setName(e.target.value);
      };
    
  • The handleNameChange function is an event handler that gets called when the user types in the input field.

  • It uses the setName function to update the name state with the current value of the input field (e.target.value).

    Clearing name:

      const clearName = () => {
        setName('');
      };
    
  • The clearName function is another event handler. When the "Clear Name" button is clicked, it calls setName(''), which sets the name state to an empty string, effectively clearing the user's name.

    Rendering Based on State:

<p>Hello, {name ? name : 'Stranger'}!</p>
  • In the component's return statement, it displays a greeting message. If name is not empty, it greets the user with their name. If name is empty, it displays "Stranger."

The useState function allows the NameForm component to manage and update its state. It provides a way to create and initialize state variables, and the associated updater function allows you to update these states while keeping the component's UI in sync with the data.

Introducing useEffect

The useEffect hook just like useState is an essential part of React. It allows you to perform "side effects" in your functional components.

What are Side Effects?

Imagine you have a remote control for your TV. When you press the buttons on the remote, like changing the channel or turning up the volume, things happen on the TV screen. You're making the TV do something. In programming, your code can also "press buttons" and make things happen outside of the code itself. These actions are called "side effects".

Side effects are actions or operations that happen outside the main flow of a program. They are typically related to interactions with the external world, such as the network, databases, or user interface, and can include things like data fetching, DOM manipulation, Asynchronous operations, etc.

The useEffect hook is specifically designed to help you manage and work with side effects in a structured and reliable way by controlling when the code runs and preventing unnecessary side effects.

Let's see how useEffectworks in the code below:

import { useState, useEffect } from 'react';
import { toast } from 'react-toastify';

function ItemList() {
  const [items, setItems] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/items');
        if (response.ok) {
        const data = await response.json();
        setItems(data);
        setIsLoading(false);
        }
      } catch (error) {
        toast.error('Error fetching data')
        setIsLoading(false);
      }
    };

    fetchData();
  }, []); 

  return (
    <div>
      <h1>Item List</h1>
      {isLoading ? (
        <p>Loading...</p>
      ) : (
        <ul>
          {items.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default ItemList;

Here's a breakdown of what useEffect is doing in the code above:

  • Data Loading: useEffect is used to initiate the data-loading process. It does so by fetching data from an external API at 'https://api.example.com/items'.

  • Asynchronous Operation: The code inside useEffect is designed to handle asynchronous operations. It uses await and async to make an API request and wait for the response.

  • Data Management: When the API response is received and is successful (response.ok), the code within the try block parses the JSON response and sets the items state using setItems with the fetched data. It also sets isLoading to false to indicate that data loading is complete.

  • Error Handling: In case there's an error during the data-fetching process, such as a network issue or a problem with the API request, catch will be executed. It displays an error message using the toast.error function from the react-toastify library and sets isLoading to false.

    The useEffect hook in this code is responsible for fetching data from an API, handling loading and error states, and updating the component's state to display either the loading message or the list of items based on the result of the data fetch.

Refresher

Before I conclude this article, I would love to go through the following bullet points again:

  • React Hooks simplify state management, replacing complex class component patterns.

useState Hook:

  • useState takes an initial state and returns an array with the current state value and a function to update it.

  • It allows you to declare and manage variables that store data like numbers, strings, objects, or arrays.

  • The component refreshes automatically when the state changes.

useEffect Hook:

  • useEffect helps manage side effects, which are actions outside the main program flow, such as data fetching or DOM manipulation.

  • It controls when the code runs and prevents unnecessary side effects.

Conclusion

useState and useEffect hooks provide a more efficient and organized way to manage state and handle side effects in React applications. These hooks make React development seamless and improve the user experience by ensuring updated and responsive interfaces.

Our journey into the world of React is far from over, We're just getting started. In the upcoming articles, we'll explore other React hooks like useContext, useRef etc showing how they can help you improve your React development.

If you liked reading this article, please comment and give me some reactions as I work on the next one! Also, If you would like to work with me please reach out to me on Twitter and I’d be happy to work with you!