Close Menu
    Latest Post

    Suspected Russian Actor Linked to CANFAIL Malware Attacks on Ukrainian Organizations

    February 22, 2026

    Trump Reinstates De Minimis Exemption Suspension Despite Supreme Court Ruling

    February 22, 2026

    How Cloudflare Mitigated a Vulnerability in its ACME Validation Logic

    February 21, 2026
    Facebook X (Twitter) Instagram
    Trending
    • Suspected Russian Actor Linked to CANFAIL Malware Attacks on Ukrainian Organizations
    • Trump Reinstates De Minimis Exemption Suspension Despite Supreme Court Ruling
    • How Cloudflare Mitigated a Vulnerability in its ACME Validation Logic
    • Demis Hassabis and John Jumper Receive Nobel Prize in Chemistry
    • How to Cancel Your Google Pixel Watch Fitbit Premium Trial
    • GHD Speed Hair Dryer Review: Powerful Performance and User-Friendly Design
    • An FBI ‘Asset’ Helped Run a Dark Web Site That Sold Fentanyl-Laced Drugs for Years
    • The Next Next Job, a framework for making big career decisions
    Facebook X (Twitter) Instagram Pinterest Vimeo
    NodeTodayNodeToday
    • Home
    • AI
    • Dev
    • Guides
    • Products
    • Security
    • Startups
    • Tech
    • Tools
    NodeTodayNodeToday
    Home»Dev»How Are Function Components Different from Classes?
    Dev

    How Are Function Components Different from Classes?

    Samuel AlejandroBy Samuel AlejandroFebruary 14, 2026No Comments13 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    src jakpky featured
    Share
    Facebook Twitter LinkedIn Pinterest Email

    How do React function components differ from React classes?

    For some time, the standard explanation was that classes offered more features, such as state management. However, with Hooks, this distinction no longer holds true.

    It is possible to hear that one type performs better than the other. Many benchmarks making such claims are flawed, so caution is advised when drawing conclusions. Performance primarily depends on the code’s actions, not whether a function or a class is chosen. Observed performance differences are typically negligible, though optimization strategies may vary slightly, as noted here.

    Regardless, it is not recommended to rewrite existing components unless there are other compelling reasons and a willingness to be an early adopter. Hooks are still relatively new, and established best practices are still emerging in tutorials.

    So, what are the fundamental differences between React functions and classes? The primary distinction lies in their mental model. This post will examine the most significant difference between them. This difference has existed since function components were introduced in 2015 but is often overlooked:

    Function components capture the rendered values.

    Let’s explore what this implies.

    Note: This post does not offer a value judgment on either classes or functions. It solely describes the distinction between these two programming models in React. For questions regarding broader adoption of functions, refer to the Hooks FAQ.

    Consider the following component:

    function ProfilePage(props) {
      const showMessage = () => {
        alert('Followed ' + props.user);
      };
     
      const handleClick = () => {
        setTimeout(showMessage, 3000);
      };
     
      return (
        <button onClick={handleClick}>Follow</button>
      );
    }

    This component displays a button that simulates a network request using setTimeout, then shows a confirmation alert. For instance, if props.user is ‘Dan’, it will display ‘Followed Dan’ after three seconds. This is a straightforward implementation.

    (It is worth noting that using arrow functions or function declarations in the example above yields identical results; function handleClick() would behave the same way.)

    How would this be written as a class? A direct translation might appear as follows:

    class ProfilePage extends React.Component {
      showMessage = () => {
        alert('Followed ' + this.props.user);
      };
     
      handleClick = () => {
        setTimeout(this.showMessage, 3000);
      };
     
      render() {
        return <button onClick={this.handleClick}>Follow</button>;
      }
    }

    It is common to assume these two code snippets are equivalent. Developers often refactor between these patterns without recognizing their implications:

    Spot the difference between two versions

    However, these two code snippets are subtly different. Examine them closely. Can you spot the difference? It can be challenging to identify at first glance.

    Spoilers are ahead, so here is a live demo if you prefer to discover it independently. The remainder of this article explains the difference and its significance.

    Before proceeding, it is important to emphasize that the described difference is unrelated to React Hooks themselves. The examples provided do not even utilize Hooks!

    The discussion centers on the fundamental difference between functions and classes in React. Understanding this distinction is beneficial for anyone planning to use functions more frequently in a React application.

    The difference will be illustrated with a common bug found in React applications.

    Open this example sandbox, which features a profile selector and the two ProfilePage implementations mentioned earlier, each rendering a Follow button.

    Perform the following sequence of actions with both buttons:

    1. Click one of the Follow buttons.
    2. Change the selected profile before 3 seconds elapse.
    3. Read the alert text.

    A peculiar difference will be observed:

    • With the ProfilePage function, clicking Follow on Dan’s profile and then navigating to Sophie’s profile would still alert ‘Followed Dan’.

    • With the ProfilePage class, it would alert ‘Followed Sophie’:

    Demonstration of the steps

    In this scenario, the first behavior is the correct one. If a user follows a person and then navigates to another person’s profile, the component should not become confused about who was followed. This class implementation clearly contains a bug.

    (It is highly recommended to follow Sophie, though.)

    So, why does the class example behave this way?

    Let’s examine the showMessage method in the class:

    class ProfilePage extends React.Component {
      showMessage = () => {
        alert('Followed ' + this.props.user);
      };

    This class method reads from this.props.user. While props in React are immutable and cannot change, ‘this’ itself is, and has always been, mutable.

    Indeed, that is the very purpose of ‘this’ in a class. React mutates it over time so that the most current version can be read in render and lifecycle methods.

    Therefore, if the component re-renders while a request is pending, this.props will change. The showMessage method then reads the user from the updated, or “too new,” props.

    This reveals an interesting insight into the nature of user interfaces. If a UI is conceptually a function of the current application state, event handlers are part of the render result—just like the visual output. Event handlers are intrinsically linked to a specific render with its particular props and state.

    However, scheduling a timeout whose callback reads this.props severs that association. The showMessage callback is not “tied” to any specific render, and thus it “loses” the correct props. Reading from ‘this’ broke that connection.

    Suppose function components did not exist. How would this problem be resolved?

    The goal would be to somehow “repair” the connection between the render with the correct props and the showMessage callback that reads them. Somewhere along the way, the props are lost.

    One solution would be to read this.props early during the event and then explicitly pass them into the timeout completion handler:

    class ProfilePage extends React.Component {
      showMessage = (user) => {
        alert('Followed ' + user);
      };
     
      handleClick = () => {
        const {user} = this.props;
        setTimeout(() => this.showMessage(user), 3000);
      };
     
      render() {
        return <button onClick={this.handleClick}>Follow</button>;
      }
    }

    This approach works. However, it makes the code significantly more verbose and prone to errors over time. What if more than a single prop was needed? What if state also needed to be accessed? If showMessage calls another method, and that method reads this.props.something or this.state.something, the exact same problem would recur. This would necessitate passing this.props and this.state as arguments through every method called from showMessage.

    Such an approach negates the ergonomic benefits typically offered by a class. It is also difficult to remember or enforce, which often leads to developers accepting bugs instead.

    Similarly, inlining the alert code inside handleClick does not address the broader issue. The aim is to structure the code in a way that allows splitting it into multiple methods while still reading the props and state corresponding to the render associated with that call. This problem is not unique to React; it can be reproduced in any UI library that stores data in a mutable object like ‘this’.

    Perhaps, methods could be bound in the constructor?

    class ProfilePage extends React.Component {
      constructor(props) {
        super(props);
        this.showMessage = this.showMessage.bind(this);
        this.handleClick = this.handleClick.bind(this);
      }
     
      showMessage() {
        alert('Followed ' + this.props.user);
      }
     
      handleClick() {
        setTimeout(this.showMessage, 3000);
      }
     
      render() {
        return <button onClick={this.handleClick}>Follow</button>;
      }
    }

    No, this does not resolve the issue. The problem stems from reading from this.props too late, not from the syntax used! However, the problem would disappear if JavaScript closures were fully utilized.

    Closures are often avoided because it is challenging to reason about a value that can be mutated over time. But in React, props and state are immutable (or at least, immutability is strongly recommended). This eliminates a major pitfall of closures.

    This implies that if props or state from a particular render are closed over, they can always be relied upon to remain precisely the same:

    class ProfilePage extends React.Component {
      render() {
        // Capture the props!
        const props = this.props;
     
        // Note: we are *inside render*.
        // These aren't class methods.
        const showMessage = () => {
          alert('Followed ' + props.user);
        };
     
        const handleClick = () => {
          setTimeout(showMessage, 3000);
        };
     
        return <button onClick={handleClick}>Follow</button>;
      }
    }

    Props are “captured” at the time of render:

    This ensures that any code within it (including showMessage) is guaranteed to access the props for that specific render. React no longer causes unexpected changes.

    It is then possible to add as many helper functions inside as desired, and they would all utilize the captured props and state. Closures provide the solution!

    The example above is correct but appears unusual. What is the purpose of a class if functions are defined inside render instead of using class methods?

    Indeed, the code can be simplified by removing the class “shell”:

    function ProfilePage(props) {
      const showMessage = () => {
        alert('Followed ' + props.user);
      };
     
      const handleClick = () => {
        setTimeout(showMessage, 3000);
      };
     
      return (
        <button onClick={handleClick}>Follow</button>
      );
    }

    As before, the props are still being captured—React passes them as an argument. Unlike ‘this’, the props object itself is never mutated by React.

    This becomes clearer if props are destructured in the function definition:

    function ProfilePage({ user }) {
      const showMessage = () => {
        alert('Followed ' + user);
      };
     
      const handleClick = () => {
        setTimeout(showMessage, 3000);
      };
     
      return (
        <button onClick={handleClick}>Follow</button>
      );
    }

    When the parent component renders ProfilePage with different props, React will call the ProfilePage function again. However, the event handler that was previously clicked “belonged” to the earlier render with its own ‘user’ value and the showMessage callback that reads it. These remain intact.

    This explains why, in the function version of this demo, clicking Follow on Sophie’s profile and then changing the selection to Sunil would alert ‘Followed Sophie’:

    Demo of correct behavior

    This behavior is correct. (Although following Sunil is also encouraged!)

    Now the significant difference between functions and classes in React is clear:

    Function components capture the rendered values.

    With Hooks, the same principle applies to state as well. Consider this example:

    function MessageThread() {
      const [message, setMessage] = useState('');
     
      const showMessage = () => {
        alert('You said: ' + message);
      };
     
      const handleSendClick = () => {
        setTimeout(showMessage, 3000);
      };
     
      const handleMessageChange = (e) => {
        setMessage(e.target.value);
      };
     
      return (
        <>
          <input value={message} onChange={handleMessageChange} />
          <button onClick={handleSendClick}>Send</button>
        </>
      );
    }

    (Here is a live demo.)

    While this is not an ideal message app UI, it illustrates the same point: if a specific message is sent, the component should not become confused about which message was actually dispatched. This function component’s message captures the state that “belongs” to the render which returned the click handler invoked by the browser. Thus, the message is set to the value present in the input when the “Send” button was clicked.

    So, functions in React capture props and state by default. But what if there is a need to read the latest props or state that do not belong to the current render? What if there is a desire to “read them from the future”?

    In classes, this is achieved by reading this.props or this.state because ‘this’ itself is mutable; React mutates it. In function components, a mutable value shared by all component renders can also be used. This is called a “ref”:

    function MyComponent() {
      const ref = useRef(null);
      // You can read or write `ref.current`.
      // ...
    }

    However, managing it falls to the developer.

    A ref serves the same purpose as an instance field. It acts as an escape hatch into the mutable imperative world. While “DOM refs” may be familiar, the concept is much more general; it is simply a container for any value.

    Visually, this.something resembles something.current. They represent the same concept.

    By default, React does not create refs for the latest props or state in function components. In many cases, they are not needed, and assigning them would be wasted effort. However, the value can be tracked manually if desired:

    function MessageThread() {
      const [message, setMessage] = useState('');
      const latestMessage = useRef('');
     
      const showMessage = () => {
        alert('You said: ' + latestMessage.current);
      };
     
      const handleSendClick = () => {
        setTimeout(showMessage, 3000);
      };
     
      const handleMessageChange = (e) => {
        setMessage(e.target.value);
        latestMessage.current = e.target.value;
      };

    If ‘message’ is read in showMessage, the message present at the time the Send button was pressed will be seen. But when latestMessage.current is read, the latest value is obtained—even if typing continued after the Send button was pressed.

    The two demos can be compared to observe the difference. A ref offers a way to “opt out” of rendering consistency and can be useful in certain situations.

    Generally, reading or setting refs during rendering should be avoided because they are mutable. The goal is to maintain predictable rendering. However, if the latest value of a particular prop or state is needed, manually updating the ref can be cumbersome. This can be automated using an effect:

    function MessageThread() {
      const [message, setMessage] = useState('');
     
      // Keep track of the latest value.
      const latestMessage = useRef('');
      useEffect(() => {
        latestMessage.current = message;
      });
     
      const showMessage = () => {
        alert('You said: ' + latestMessage.current);
      };

    (Here is a demo.)

    The assignment is performed inside an effect so that the ref value changes only after the DOM has been updated. This ensures that the mutation does not disrupt features like Time Slicing and Suspense, which rely on interruptible rendering.

    Using a ref in this manner is not frequently necessary. Capturing props or state is typically a better default. However, it can be useful when working with imperative APIs such as intervals and subscriptions. Remember that any value can be tracked this way—a prop, a state variable, the entire props object, or even a function.

    This pattern can also be beneficial for optimizations, such as when useCallback identity changes too frequently. Nevertheless, using a reducer is often a superior solution. (This will be a topic for a future blog post!)

    In this post, a common problematic pattern in classes was examined, along with how closures help resolve it. However, it might be noticed that optimizing Hooks by specifying a dependency array can lead to bugs with stale closures. Does this imply that closures are the problem? This is not necessarily the case.

    As demonstrated, closures actually help fix subtle problems that are difficult to detect. Similarly, they simplify writing code that functions correctly in Concurrent Mode. This is possible because the logic within the component closes over the correct props and state with which it was rendered.

    In all observed instances, “stale closures” problems arise from the mistaken assumption that “functions don’t change” or that “props are always the same”. This is incorrect, as this post aims to clarify.

    Functions close over their props and state, making their identity equally important. This is not a bug but a feature of function components. Functions should not be excluded from the “dependencies array” for useEffect or useCallback, for example. (The correct fix is usually either useReducer or the useRef solution discussed above—guidance on choosing between them will be documented soon.)

    When writing the majority of React code with functions, an adjustment in intuition is required regarding optimizing code and what values can change over time.

    As Fredrik stated:

    The most effective mental rule discovered so far with Hooks is “code as if any value can change at any time”.

    Functions are no exception to this rule. It will take time for this to become common knowledge in React learning materials. It necessitates some adaptation from the class mindset. However, this article aims to provide a fresh perspective.

    React functions consistently capture their values—and now the reason is clear.

    They are a fundamentally different approach.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleA17 Pro vs. A16 Bionic: A Detailed Comparison of Apple’s Mobile Chips
    Next Article Building Prometheus: Backend Aggregation for Gigawatt-Scale AI Clusters
    Samuel Alejandro

    Related Posts

    Dev

    Docker vs Kubernetes in Production: A Security-First Decision Framework

    February 21, 2026
    Dev

    Effortless VS Code Theming: A Guide to Building Your Own Extension

    February 19, 2026
    Dev

    Implementing Contrast-Color Functionality Using Current CSS Features

    February 19, 2026
    Add A Comment
    Leave A Reply Cancel Reply

    Latest Post

    ChatGPT Mobile App Surpasses $3 Billion in Consumer Spending

    December 21, 202513 Views

    Creator Tayla Cannon Lands $1.1M Investment for Rebuildr PT Software

    December 21, 202511 Views

    Automate Your iPhone’s Always-On Display for Better Battery Life and Privacy

    December 21, 202510 Views
    Stay In Touch
    • Facebook
    • YouTube
    • TikTok
    • WhatsApp
    • Twitter
    • Instagram
    About

    Welcome to NodeToday, your trusted source for the latest updates in Technology, Artificial Intelligence, and Innovation. We are dedicated to delivering accurate, timely, and insightful content that helps readers stay ahead in a fast-evolving digital world.

    At NodeToday, we cover everything from AI breakthroughs and emerging technologies to product launches, software tools, developer news, and practical guides. Our goal is to simplify complex topics and present them in a clear, engaging, and easy-to-understand way for tech enthusiasts, professionals, and beginners alike.

    Latest Post

    Suspected Russian Actor Linked to CANFAIL Malware Attacks on Ukrainian Organizations

    February 22, 20260 Views

    Trump Reinstates De Minimis Exemption Suspension Despite Supreme Court Ruling

    February 22, 20260 Views

    How Cloudflare Mitigated a Vulnerability in its ACME Validation Logic

    February 21, 20260 Views
    Recent Posts
    • Suspected Russian Actor Linked to CANFAIL Malware Attacks on Ukrainian Organizations
    • Trump Reinstates De Minimis Exemption Suspension Despite Supreme Court Ruling
    • How Cloudflare Mitigated a Vulnerability in its ACME Validation Logic
    • Demis Hassabis and John Jumper Receive Nobel Prize in Chemistry
    • How to Cancel Your Google Pixel Watch Fitbit Premium Trial
    Facebook X (Twitter) Instagram Pinterest
    • About Us
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    • Disclaimer
    • Cookie Policy
    © 2026 NodeToday.

    Type above and press Enter to search. Press Esc to cancel.