Why React Hooks: Enhancing Code Performance and Readability

Discover what React Hooks are, why developers choose them over class components, and learn how to convert your SPFx projects to React Hooks in this article.

By Last Updated: November 20, 2024 8 minutes read

Most React developers use React Hooks to create React apps, as evidenced by documentation, examples, and videos on YouTube. However, this is not the case in the Microsoft 365 development space, where SharePoint Framework (SPFx) developers still primarily use class components. This is likely due to new SPFx projects defaulting to class components, which have been one of the last holdouts.

Even new Microsoft Teams projects created with YO Teams in the Visual Studio Teams Toolkit use Hooks. Although I was late to switch over to Hooks, I quickly saw why so many others had made the switch. If you haven’t made the switch yet, this is for you.

In this article, I’ll explain why functional components enabled by Hooks are better than class components, and why I prefer Hooks even more. For SPFx developers, I will also show you how to quickly convert your new SPFx web part projects to React Hooks.

The React community evolved to favor React Hooks

For a long time, developers built React apps using class components. These components were used to manage state and side effects. Functional components, on the other hand, were only used for displaying static UX components.

Prior to React v16.8, React’s API only supported class components. However, when Hooks were introduced in v16.8, they were like a cheat code that unlocked functional components and greatly reduced and simplified React component design and code.

Functional components have become the preferred way of writing components in React for several reasons:

  1. Simplicity: Functional components are easier to read and understand. They’re just JavaScript functions that take properties as input and return JSX or TSX as output. Class components, on the other hand, can be much more verbose with a lot of boilerplate code, such as a constructor, lifecycle methods, and binding functions.
  2. Reusability: Functional components are more reusable because they can easily be composed and combined to create more complex components. They promote the use of composition over inheritance and encourage a more modular and scalable code structure.
  3. Performance: Functional components can be more performant than class components due to their nature as pure functions. They don’t have the overhead of managing instances and lifecycle methods. Additionally, functional components can be optimized with React’s memoization and Hooks APIs to prevent unnecessary re-renders.
  4. Better patterns and code organization: Functional components encourage the use of modern JavaScript patterns like arrow functions and destructuring. They also promote better code organization by separating concerns into smaller functions, making the codebase easier to navigate, consume, and maintain.

It’s worth noting that class components are still fully supported in React, and there may be situations where they are more appropriate, such as when using certain third-party libraries or working with legacy code bases. The decision of whether to use functional or class components ultimately depends on the specific needs and requirements of the project and the developer.

Why do we need React Hooks?

Functional components enable the use of Hooks, and vice versa. Hooks provide a simpler and more flexible way to handle state, lifecycle, and side effects in React components.

Developers can reuse stateful logic across multiple components using Hooks, eliminating the need for higher-order components or rendering property patterns.

Info: Debate: To use, or not use, React Hooks!
☑️ Still not sure you’re convinced Hooks are the greatest thing since IntelliSense for web developers? Not everyone agrees with me! Before I dive into how Hooks work, I want to call out I had a great conversation with my friend Julie Turner who has a different perspective on Hooks. We discussed two different mindsets to Hooks from season 1 of our show on YouTube: CloudDev Clarity | Episode 9: React Hooks.

So how do React Hooks work?

The fundamental concept of Hooks is memoization. Memoization is a technique used to optimize component performance by caching the results of computationally expensive operations and re-computing them only when necessary. Hooks are implemented using React’s memo API or the useMemo() hook. Memoizing large datasets or expensive operations can significantly improve component performance and reduce React’s workload.

Hooks - useMemo()

Although it may not be used frequently, let’s first examine the useMemo() Hook because it serves as the foundation for many of the other Hooks we’ll explore.

The useMemo() hook is used to memoize a value and prevent unnecessary re-renders. This hook is typically used when a computationally expensive operation needs to be performed, such as filtering a large dataset or performing a complex calculation.

In the example, the useMemo() hook is used to memoize the filteredItems value. The function passed to the useMemo() hook filters the items array based on the searchTerm value and returns a new array containing only the items that match the search term.

import React, { useState, useMemo } from "react";

function App() {
  const [searchTerm, setSearchTerm] = useState("");
  const [items, setItems] = useState([
    'blue peacock',
    'acei',
    'frontosa',
    'tretocephalus',
    'electric blue',
    'electric yellow',
    'venustus',
    'demasoni'
  ]);

  // memoize filtered items
  const filteredItems = useMemo(() => {
    return items.filter((item) => item.includes(searchTerm));
  }, [items, searchTerm]);

  return (<div>
    <input type="text"
           value={searchTerm}
           onChange={(e) => setSearchTerm(e.target.value)}
    />
    <ul>
      {filteredItems.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  </div>);
}

export default App;

The useMemo() Hook is passed an array of dependencies, which includes the items array and the searchTerm value. This means that the function will only be re-executed if either the items array or the searchTerm value changes.

By using the useMemo() hook to memoize the filteredItems value, we can prevent unnecessary re-renders of the component when the searchTerm value changes, which can improve performance and reduce the amount of work that React needs to do.

Hooks - useState()

React provides many Hooks out of the box, but one of the most commonly used is useState().

The useState() Hook adds state to functional components in React. It returns an array with two values: the current state value and a function to update the state value.

In this example, we use the useState() Hook to initialize the count state variable to 0. The setCount function returned by the useState() hook is used to update the count state variable when the button is clicked.

import React, { useState } from 'react';

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

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

  return (
    <div>
      <p>You clicked the button {count} times</p>
      <button onClick={increment}>Click me</button>
    </div>
  );
}

export default Counter;

When the button is clicked, the increment() function is called, which in turn calls the setCount() function with the new count value. React then re-renders the component with the updated count value.

Hooks - useEffect()

Next up is the useEffect() hook.

In React, the useEffect() Hook is used to add side effects to functional components. Side effects are actions that affect something outside of the component, such as fetching data from an API, manipulating the DOM, or setting up an event listener.

In this example, we use the useEffect() Hook to fetch data from an API using Axios. The setUsers() function is used to update the users state variable with the fetched data.

The second argument of the useEffect() Hook is an empty array, which means that the side effect will only run once, when the component mounts. If we wanted the side effect to run whenever a specific state variable changes, we could add that variable to the array of dependencies.

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios.get('https://jsonplaceholder.typicode.com/users');
      setUsers(result.data);
    };
    fetchData();
  }, []);

  return (
    <div>
      <h2>User List</h2>
      <ul>
        {users.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default UserList;

Hooks - useCallback()

In React, the useCallback() Hook can be used to memoize a function and prevent it from being recreated on each render.

This can be useful in scenarios where a component passes a function down to a child component as a property and that function is recreated on each render. This can cause unnecessary re-renders of the child component. The first argument of the useCallback() Hook is the function that needs to be memoized. The second argument is an array of dependencies that the function relies on.

If any of the dependencies change, the function will be recreated.

In this example, the Parent component is passing the handleClick function down to the Child component as a prop. Since the handleClick function relies on the setCount() function, which is a dependency that could change on each render, it could cause unnecessary re-renders of the Child component.

import React, { useCallback, useState } from "react";
import Child from "./Child";

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

  const handleClick = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <Child handleClick={handleClick} />
    </div>
  );
}

export default Parent;

However, by using the useCallback() Hook, we ensure that the handleClick function is only re-created when the dependencies in the array change.

In this case, there are no dependencies, so the function is only created once and the Child component won’t re-render unnecessarily.

Using React Hooks in Microsoft 365 Projects

If you’re working with Microsoft Teams apps, Hooks have already been incorporated into the tools for creating them, so there’s no extra work needed on your part.

However, for SPFx projects, it only takes a few minutes to update a default SPFx React-based web part project to use Hooks.

Check out my article on how change those SPFx React web part projects to use Hooks: How to use React Hooks with the SharePoint Framework (SPFx).

What are your thoughts?

If you’re still using class components for your React apps, are you interested in giving Hooks a try? Let me know! Leave a comment below 👇!