React Function is different from React Classes.

Previously, a standard answer was to say that classes provided more functionality (such as state). That is not the case with Hooks.

You’ve probably heard that one of them is better. Which one? Many of these performance benchmarks are flawed, so I’m careful to draw conclusions from them. Performance depends primarily on the code rather than choosing a function or a class. In our observations, even though the optimization strategy was different, the difference in performance was minimal.

On the other hand, we don’t recommend rewriting your written components unless you have a reason and don’t mind being an early adopter. Hooks are still very new (like React in 2014), and some “best practices” have not yet been written into tutorials.

Is there a fundamental difference between React Function and classes? Of course, they’re — in the mental model. In this article, I’ll look at the biggest differences between them. This was introduced in 2015’s Function Components, but it’s often overlooked:

The Function component captures the value after render.

Let’s analyze what this means.


Note: this article does not measure the value of classes or functions; I only describe the differences between the two programming models in React. For more on adopting functions, see Hooks FAQ.


Consider this component:

function ProfilePage(props) {
  const showMessage = (a)= > {
    alert('Followed ' + props.user);
  };

  const handleClick = (a)= > {
    setTimeout(showMessage, 3000);
  };

  return (
    <button onClick={handleClick}>Follow</button>
  );
}
Copy the code

It shows a button with a setTimeout that simulates the network request and then materializes in the confirmation popup. For example, if the props. User is ‘Dan’, it will show ‘Followed Dan’ three seconds later, very simple.

(Note that whether I used arrows or normal functions in the example above,function handleClick()It must have the same effect.

What if we write it as class? A direct translation might look like this:

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

  handleClick = (a)= > {
    setTimeout(this.showMessage, 3000);
  };

  render() {
    return <button onClick={this.handleClick}>Follow</button>; }}Copy the code

These two pieces of code are generally considered equivalent, and people often refactor randomly in these patterns without noticing what they mean:

However, the two pieces of code are slightly different. Take a good look at them. Do you see the difference? Personally, it took me a while to find out.

This is an online demo if you want to find out for yourself. The following sections of the article analyze this difference and why it is so.


Before we continue, I want to stress that the differences I describe have nothing to do with React Hooks themselves; the above example doesn’t even need Hooks!

This is all about the difference between functions and classes in React, which you might want to learn if you plan to use functions more often in React.


We’ll illustrate this difference with a common bug in React applications.

Use the current item selector and the previous two ProfilePage implementations to open the sandbox example — one Follow button per render.

Use two buttons in this order:

  1. Click on one of the buttons.
  2. Change the selection within 3 seconds.
  3. Look at the text that pops up.

You’ll notice a special difference:

  • When the ProfilePage is function, click on the Follow Dan entry and switch to Sophie, still popping up ‘Followed Dan’.

  • When class ProfilePage, it pops ‘Followed Sophie’ :


In this case, the first behavior is correct. If you follow one person and then switch to another person’s entry, my component should not be confused about who I’m following. The implementation of class is clearly a mistake.


So why does our class example work this way?

Let’s take a closer look at the showMessage method in class:

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

The class method reads this.props. User, props is immutable in React. But this is, and has changed.

In fact, that’s the purpose of having this in class. React itself will mutate over time so that you can get new versions in the render and lifecycle.

So if we re-render the component while it is in the request state, this.props will change. The showMessage method gets the user from the “too new” props.

This exposes an interesting phenomenon in the nature of the UI layer. If we say that the UI is conceptually a function of the current application state, then the event handler is part of the rendered result — just like the visual output. Our event handler “belongs” to a specific render with specific props and state.

However, scheduling a timeout back to read this.props breaks the connection. Our showMessage callback is not “bound” to any particular render, so it “loses” the correct props, and reading this breaks the link.


The Function component does not have this problem. How do we solve this problem?

We want to somehow “fix” the connection between render with the right props and the showMessage callback that gets them. This way props will lose track.

One way is to read this.props early in the event and explicitly pass them to the timeout 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>; }}Copy the code

It works. However, this approach makes the code significantly more redundant and error-prone over time. What if we need more than one prop? What if we also need to get state? If showMessage calls another method and that method reads this.props. Something or this.state. Something, we run into the same problem again. So we have to pass this. Props and this. State as arguments to every method that calls showMessage.

Doing so often breaks the ergonomics typically provided by a class and is difficult to remember or enforce, which is why you often have bugs.

Similarly, putting an alert into handleClick does not solve this problem. We want to structure the code in a way that allows more methods to be split, and we also want to read the corresponding props and state of the render associated with the call. This problem isn’t even unique to React — you can reproduce it in any UI library that puts data into mutable objects like this.

Perhaps we can use the constructor bind method?

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>; }}Copy the code

No, you can’t fix anything like that. Remember, the problem was that we were too late to read this.props — not that we used the syntax this way! However, this problem can be solved if we rely entirely on JavaScript closures.

Closures are generally avoided because it is difficult to know what values might vary over time. In React, props and state cannot be changed! (Or at least, it’s a strong recommendation.) This removes one of the killer features of closures.

This means that if you block the props or state of a particular render, you can always get the same thing:

Class ProfilePage extends React.Component {render() {// hold props! const props = this.props; // This is not a class method. const showMessage = () => { alert('Followed ' + props.user); }; const handleClick = () => { setTimeout(showMessage, 3000); }; return <button onClick={handleClick}>Follow</button>; }}Copy the code

You’ve “captured” props in render:

This way, any code inside it (including showMessage) can guarantee that the props of this particular render won’t “move our cheese” anymore.

We can add as many helper methods to it as we want, and they all use the captured props and state to save the closure.


The above example is not wrong but looks strange. If you define a function in render instead of using a class method, why bother with class?

In fact, we can simplify the code by removing the class shell:

function ProfilePage(props) {
  const showMessage = (a)= > {
    alert('Followed ' + props.user);
  };

  const handleClick = (a)= > {
    setTimeout(showMessage, 3000);
  };

  return (
    <button onClick={handleClick}>Follow</button>
  );
}
Copy the code

As above, the props are still captured — React passes them as arguments. Unlike this, the props object itself is not changed by React.

This is more obvious if the function is defined:

function ProfilePage({ user }) {
  const showMessage = () => {
    alert('Followed ' + user);
  };

  const handleClick = () => {
    setTimeout(showMessage, 3000);
  };

  return (
    <button onClick={handleClick}>Follow</button>
  );
}
Copy the code

When the parent component renders the ProfilePage with different props, React calls the ProfilePage method again. But the event we clicked on “belonged” to the last Render with its own user value and the showMessage callback that read it, both intact.

That’s why, in the function version of this demo, clicking Follow on Sophie’s entry and switching to Sunil brings up ‘Followed Sophie’ :

This reaction is correct. (Though you might want to keep an eye on Sunil, too!)


React now we know the biggest difference between functions and classes:

The Function component captures rendered values.

Using Hooks, the same principle applies to state. Consider this example:

function MessageThread() {
  const [message, setMessage] = useState(' ');

  const showMessage = (a)= > {
    alert('You said: ' + message);
  };

  const handleSendClick = (a)= > {
    setTimeout(showMessage, 3000);
  };

  const handleMessageChange = (e) = > {
    setMessage(e.target.value);
  };

  return (
    <>
      <input value={message} onChange={handleMessageChange} />
      <button onClick={handleSendClick}>Send</button>
    </>
  );
}
Copy the code

[This is an online demo.] )

While this is not a good messaging application UI, it implements the same thing: if I send a specific message, the component should not be confused about which message to send. The message from this function component captures state and “belongs” to return render called by the browser click event. So the message is set to the value in the input when I click “send”.


So we know that functions in React capture props and state by default. But what if we want to read the latest props or state that is not part of a specific render? What if we want to read them in the future?

In classes, you can read this.props or this.state, because this itself is mutable and React will change it. In the function component you can also have a render variable that is shared for all renders called “ref” : render renders renders for touch.

function MyComponent() {
  const ref = useRef(null);
  // You can read and write 'ref.current'.
  // ...
}
Copy the code

However, you need to manage it yourself.

The ref field plays the same role as the instance field; it is an escape hatch into the mutable command world. You may be familiar with “DOM refs,” but this principle is much more mundane, just a box that you can put things in.

Even visually, this.someting looks like a mirror image of something. Current. They represent the same concept.

By default, React does not create refs for the latest props or state in the function component. In many cases they are not needed, and assigning them would be a wasteful effort. However, you can manually track values if you wish:

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;
  };
Copy the code

If we read the Message of showMessage, we see the message when we send the button. But when we read latestmessage.current, we get the most recent value — even if we continue typing after pressing the send button.

You can compare these two demos and see the difference. Ref is an “opt-out” render consistency method that can be handy in some cases.

In general you should avoid reading or setting refs during rendering because they are mutable. We wanted to keep the rendering predictable. However, if we want to get the latest value of a particular prop or state, manually updating the ref can be cumbersome. We can automate it with effect:

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

(This is a demo.)

We assign values in effect to make the ref value change only when the DOM is updated. This ensures that our variations don’t break interrupt rendering features like Time Slicing and Suspense.

Refs are rarely used like this, capturing props or state is better by default. However, it comes in handy when dealing with tricky APIs like timers and subscriptions. Remember that you can track any of these values — prop, the state variable, the entire props object, or even a function.

This pattern can also be used for optimization — for example, when the useCallback flag changes frequently. However, using a Reducer is usually a better solution. (That will be covered in a future blog post!)


In this article, we looked at the common pattern of breaking classes and how closures can help us fix it. However, you may have noticed that when you try to optimize Hooks by specifying dependent arrays, you may encounter bugs from outdated closures. Does that mean closures are the problem? I don’t think so either.

As we’ve seen before, closures do help us fix subtle problems that are hard to notice. As such, they make it easier to write code that works in concurrent mode. This is probably because the logic inside the component blocks the correct props and state after rendering.

In all the cases seen so far, the “stale closure” problem occurs because of the false assumption that “functions do not change” or that “props are always the same.” This is not the case, and I hope this article helps clarify that.

Functions lock their props and state — all that matters is what they are. This is not a bug, but a feature of the function component. For example, Functions should not be excluded from the “dependent array” of userEffect or useCallback. (The common appropriate fixes mentioned above, be they useReducer or useRef solutions — we’ll explain how to choose between them in the documentation shortly)

When we write most React code in functions, we need to adapt our ideas about optimizing code and what values change all the time.

The best mental rule I’ve found so far with hooks is that “any value in code can change seemingly at any time”.

Functions are no exception. It will take some time for this to become common knowledge in React learning materials. Moving from the class mentality will take some getting used to, but I hope this article will help you see it in a new light.

React Functions always capture their values — and now we know why.

They are an entirely different Pokemon.

How Are Function Components Different from Classes? (2019-03-03)