Upgrading to React 18: A Comprehensive Guide to Transitioning from Class Components to Hooks

React has evolved dramatically since its inception. With the recent release of React 18, there’s a compelling case for upgrading your older class components to the newer functional components using hooks. This blog post aims to guide you through this transition by answering the ‘why’ and explaining the ‘how’.

Why Upgrade to React 18?

Concurrent Rendering

React 18 introduces concurrent rendering, which allows React to perform multiple tasks simultaneously without blocking the main thread. This is particularly beneficial for larger applications, as it results in a smoother user experience.

Improved Performance

The architecture enhancements in React 18 enable the library to better prioritize tasks, speeding up rendering times and thus improving overall application performance.

Embracing Hooks

Though hooks were already introduced in React 16.8, React 18 takes them to the next level by fully incorporating them as a primary method to manage state and side effects.

Transitioning from Class Components to Functional Components with Hooks

Step 1: Install React 18

Before diving into any code changes, make sure you’ve updated to React 18. Run the following command in your terminal:

npm install react@18 react-dom@18

or 

yarn add react@18 react-dom@18

Step 2: Refactor Class Components to Functional Components

Here’s a simple example - let’s convert a class component to a functional component using hooks.

Class Component:

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

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

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

Functional Component:

import { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  
  const increment = () => {
    setCount(count + 1);
  }

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

Step 3: Replace Lifecycle Methods with useEffect

Lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount can be replaced with the useEffect hook.

Example:

import { useEffect } from 'react';

useEffect(() => {
  console.log('Component mounted or updated');

  return () => {
    console.log('Component will unmount');
  };
}, []);

Step 4: Deconstructing Props In class components, you often deconstruct props in the render() method. In functional components, you can deconstruct them directly in the parameter list, making the code cleaner and more concise.

Class Component:

class Welcome extends React.Component {
  render() {
    const { name, email, phone } = this.props;
    return <h1>Hello, {name}</h1>;
  }
}

Functional Component:

function Welcome({ name, email, phone }) {
  return <h1>Hello, {name}</h1>;
}

Conclusion

Upgrading to React 18 and transitioning from class components to hooks might seem like a tall order, but the benefits in terms of performance, readability, and maintainability make it an endeavor worth undertaking. Take the plunge and embrace the future of React development!

Victory Lap - Measure Your Performance Benefit

To measure the benefits of your upgrade, utilize React’s built-in Profiler. It helps you understand how often your application renders and the “cost” of rendering.

Installing React DevTools Add React DevTools as a browser extension for Chrome, Firefox, or as a standalone app.

Start Profiling Open React DevTools. Switch to the Profiler tab. Click the “Record” button and interact with your app. Click “Stop” once done.

Analyzing Results

Flamegraph: Shows the “cost” of rendering each component.

Ranked Chart: Lists the components that took the most time to render.

Component Chart: Offers an in-depth look at a single component.

Interactions: If you’ve wrapped your application with the Profiler API and passed an id, this panel will display a log of those specific events. Interpret Results

Optimize expensive components using techniques like memoization. Reduce unnecessary re-renders with React.memo or shouldComponentUpdate.

Utilize React 18’s concurrent features like useDeferredValue or startTransition for better UX.

With these additional steps, your readers will not only learn how to transition to React 18 but also how to measure and optimize their application’s performance.

References:

[React.dev https://react.dev/blog/2023/03/16/introducing-react-dev]
[React 18: New Features and Performance https://react.dev/blog/2022/03/08/react-18-upgrade-guide#new-apis-for-libraries]
[React DevTools https://react.dev/learn/react-developer-tools]

Buck O'Leary

Writer / Full Stack Developer / Former DBC Coach + Vets Who Code Mentor / One of dozens of living Wizards fans / 4.9 Stars on WAG!