React.js Explained: Comparing and Converting Higher-Order Components to Hooks with Practical Examples

React.js Explained: Comparing and Converting Higher-Order Components to Hooks with Practical Examples

This article simplifies the complex concept of Higher-Order Components (HOCs) and Hooks in React, illuminating their differences with clear code examples.

Target Audience

If you're a React developer looking to elevate your understanding of Higher-Order Components (HOCs) and Hooks, or if you're just embarking on your React.js journey, this enriching guide is tailored for you. We're about to delve into these critical concepts with ample real-world examples and engaging code samples that will help you grasp their practical applications.

Learning Objectives

By journey's end, you'll be adept at:

  1. Identifying Higher-Order Components in your React application.

  2. Crafting and deploying Higher-Order Components effectively.

  3. Leveraging React Hooks as an alternative to HOCs.

So, brace yourself for this exciting adventure!

Introduction

While exploring React.js for professional development, I discovered a valuable resource: an E-book about React patterns. This book dissected the different patterns used in React, explaining their importance in the tech industry. One concept I found particularly intriguing was the Higher-Order Component, which operates similarly to a higher-order function in JavaScript.

Diving into Higher-Order Components

Have you ever wondered about the Higher-Order Function concept in JavaScript? If so, you'll find the Higher-Order Component (HOC) concept in React.js fascinating!

In layman's terms, a Higher-Order Component (HOC) is a function that graciously accepts another component as an input and spits out a brand new, more capable component. The strength of HOCs lies in their ability to minimize code repetition, keeping your codebase lean and mean.

To illustrate, consider an e-commerce app with various components needing the same API calls. Alternatively, visualize multiple components that demand identical business logic, say, user authentication. Here, HOCs strut in as a superhero, eliminating the repetition and keeping your code DRY (Don't Repeat Yourself).

Let's dive into a concrete example. We'll use the popular Pokémon API to showcase an HOC in action.

import React from "react";

const withData = (Component) => {
  class WithData extends React.Component {
    state = {result: []};

    componentDidMount() {
      this.fetchData();
    }

    fetchData = async () => {
      const response = await fetch("https://pokeapi.co/api/v2/pokemon");
      const data = await response.json();
      this.setState({result: data.results});
    };

    render() {
      return <Component results={this.state.result} />;
    }
  }

  return WithData;
};

export default withData;

The withData function defined above is our hero HOC. It accepts a component and makes an API call to fetch Pokémon data. The fetched data is then added to the local state and passed as a prop to the child component.

Let's see withData in action:

import withData from "./withData";

const ListResults = ({ results }) => (
  <ul>
    {results.map((pokemon) => (
      <li key={pokemon.name}>{pokemon.name}</li>
    ))}
  </ul>
);

export default withData(ListResults);

Our ListResults component receives the fetched Pokémon list from withData and renders it beautifully on the screen. No sweat!

Adventures with React Hooks

React Hooks enable function components to harness React's state and lifecycle features without having to convert to class components, making your code more readable and manageable.

Let's reimagine our previous example using Hooks this time:

import { useEffect, useState } from "react";

const useData = () => {
  const [results, setResults] = useState([]);

  const fetchData = async () => {
    const response = await fetch("https://pokeapi.co/api/v2/pokemon");
    const data = await response.json();
    setResults(data.results);
  };

  useEffect(() => {
    fetchData();
  }, []);

  return results;
};

export default useData;

The useData Hook functions just like the withData HOC. It fetches Pokémon data and stores it locally using the useState Hook. The useEffect Hook ensures the data fetch occurs when the component mounts.

Let's couple this Hook with our ListResults component:

import useData from "./useData";

const ListResults = () => {
  const results = useData();

  return (
    <ul>
      {results.map((pokemon) => (
        <li key={pokemon.name}>{pokemon.name}</li>
      ))}
    </ul>
  );
};

export default ListResults;

Notice how clean and straightforward this is? ListResults calls useData, which returns the fetched data. Contrary to HOCs, Hooks don't pass data via props, leading to simpler code.

React Hooks have quickly become a developer's favorite, thanks to their simplicity, readability, and testability. They facilitate separation of concerns, resulting in a cleaner and more maintainable codebase.

Wrapping Up

We've delved into the world of Higher-Order Components and Hooks, explored their differences, and seen them in action through real-world examples. Armed with this knowledge, you're now empowered to identify, create, and implement both HOCs and Hooks in your React applications. Plus, you're equipped to convert an HOC into a Hook in your React projects, bringing simplicity and elegance to your code.

Let's continue creating extraordinary applications with React!