← Back to React Course | Chapter 5: Core Hooks | Lesson 11 of 12

Hooks vs Class Lifecycle Methods

Hooks let a function component do everything a class component could, using simple functions instead of the more ceremonial class syntax.

State: this.state vs useState

A class component stores state as an object on this.state and updates it via this.setState(), which merges the given fields into the existing state object. useState instead gives you one independent state variable and setter pair per call, and does NOT automatically merge objects.

Note: If you're translating class code to Hooks, remember each this.state field often becomes its own separate useState call.

Warning: Unlike this.setState, calling a useState setter with an object REPLACES the whole value -- it does not merge it with the previous object automatically.

Example: State: this.state vs useState

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    // Hooks version
    function Counter() {
      const [count, setCount] = React.useState(0);
      return <button onClick={() => setCount(count + 1)}>{count}</button>;
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<Counter />);
  </script>
</body>
</html>

Lifecycle Methods vs useEffect

Class components split side-effect logic across several lifecycle methods: componentDidMount (runs once), componentDidUpdate (runs on updates), and componentWillUnmount (runs on removal). useEffect combines all three roles into one Hook, distinguished by its dependency array and optional cleanup function.

Note: An effect with [] roughly matches componentDidMount + componentWillUnmount combined; an effect with dependencies roughly matches componentDidUpdate too.

Warning: The match between useEffect and lifecycle methods is close but not perfect -- effect timing (after paint) differs slightly from componentDidMount's timing.

Example: Lifecycle Methods vs useEffect

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function MountLogger() {
      React.useEffect(() => {
        console.log("Like componentDidMount");
        return () => console.log("Like componentWillUnmount");
      }, []);
      return <p>Check the console.</p>;
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<MountLogger />);
  </script>
</body>
</html>

Why Function Components Are Now Preferred

Hooks let function components do everything class components can, with less boilerplate (no constructor, no this binding issues) and easier logic reuse via custom Hooks. Because of this, the official React docs and most new projects favor function components with Hooks over classes.

Note: When starting a new React project today, default to function components with Hooks -- reserve class components only for maintaining existing legacy code.

Warning: this binding issues (like event handlers losing their this context) are a common class-component pitfall that Hooks avoid entirely.

Example: Why Function Components Are Now Preferred

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function ModernComponent() {
      const [liked, setLiked] = React.useState(false);
      return <button onClick={() => setLiked(!liked)}>{liked ? "Liked!" : "Like"}</button>;
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<ModernComponent />);
  </script>
</body>
</html>
Common Mistakes
  1. Assuming Hooks and class lifecycle methods map one-to-one in every detail -- some behaviors (like effect timing) differ subtly.
  2. Mixing class-based patterns (like this.state) into a function component by mistake.
  3. Believing class components are deprecated or unsupported -- they still work fine, just aren't the recommended style for new code.
Chapter Summary
  • Hooks let function components use state and other React features previously only available in classes.
  • useState replaces this.state/this.setState.
  • useEffect replaces most uses of componentDidMount, componentDidUpdate, and componentWillUnmount combined.
  • Function components with Hooks are now the recommended default in React.
Browser Support

Hooks were introduced in React 16.8; class components remain fully supported in React 18.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.