Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

If I understand correctly, the team is pretty clear that if they had written React from scratch today, they would have gone with functional components and not class components.

The major issue is life cycle. When is a class component instance created or destroyed? The answer is "whenever the framework feels like it," and that's a really ugly model for instance life cycle. Additionally and most importantly, it means that member variables on a React class component instance don't work the way that members are expected to work; you just can't rely on them having any particular value because the framework can create or destroy an instance when it feels like it for performance reasons. What good is a class and instance abstraction if when you try to use member variables (one of the key features of object-oriented programming) you're highly likely to just break the model in hard-to-debug ways?

Because the framework isn't using class component instances like instances are supposed to be used, the React team is tossing that model to the curb.



sort by: page size:

This seems at least part of it. And you don’t even really have to learn those things to use stateful React class components, they’re very seldom deeply inherited or require any `this` besides lifecycle methods and their own implementation. Rendered components are not instances.

I know there’s an aversion to classes in a lot of the current React community but the Component design in React was really much easier to reason with.


My superficial understanding is that React components as classes are far more complicated than they might seem at first. You're not in control of creating the class instances, rendering them or destroying them. React is in control of that.

The constructor is the wrong place for data fetching in React class components as far as I remember anyway. I don't remember the exact reason, but it always has been somewhat non-obvious and you always needed to understand the React lifecycle to avoid bugs in that kind of code.


I don't think the issue is "what is a class". It's more that the lifecycle functions get tied into the class, and you end up with a ball of highly conditional logic. It's easy for newbies to just add stuff to make it work, but end up with a mess.

React is the view layer, so the class concept of "here's some data and methods to alter it" doesn't really match with what happens - I don't know that components every should have been classes in the first place.


Still, it isn't entirely obvious under what conditions the methods of a React Component class are going to be called. For all you know they might always be called against the instance.

So when react components were classes they were comparable, but now that react components are functions they are not?

This was more explicit when implementing class based components than functional components because the class based API had explicit overridable methods for those aspects of the component lifecycle.

That issue probably wouldn’t exist except that React has to make some concessions to performance, therefore component reuse, therefore component state working in sometimes unintuitive ways.


>Conceptually, a component seems better represented by an object/class than a procedure/function.

In other paradigms, it is! Our paradigm is exploring the functional take. I agree it's a bit unorthodox but we are very intentional about modeling it that way. It really has a bunch of powerful properties one might not expect.

>And aren't classes just really functions under the hood anyways?

The key difference is that in React, UI is a pure projection of current data (props/state). You're always supposed to "return" the UI. Sure a class is a function, but that function is invoked once. Its methods can be called many times, but having a pure render() method (like in class-based React) is really a class cosplaying as a function. Functions are more honest to what React is trying to be.

Relevant part from Seb's Hooks RFC comment (https://github.com/reactjs/rfcs/pull/68#issuecomment-4393148...):

> Classes may seem like the ideal thing to hold state since that's what they're designed for. However, React is more written like a declarative function that keeps getting executed over and over to simulate it being reactive. Those two things have an impedence mismatch and that keeps leaking when we think of these as classes.

>Another issue is that classes in JS merge both methods and values on the same namespace. This makes it very hard to make optimizations because sometimes methods behave like static methods and sometimes behave like values that contain functions. The Hooks pattern encourages the use of more statically resolvable calls for helper functions.

>In classes, each method has its own scope. It causes issues like us having to reinvent default props so that we can create a single shared resolved object across those. You also encourage sharing data between those methods using mutable fields on the class since the only shared thing is this. This is also problematic for concurrency.

>Another issue is just that the conceptual mental model for React is just functions calling other functions recursively. There is a lot of value to express it in those terms to help build the correct mental model.

For a concrete example of where classes as a model fails us, consider useTransition (https://react.dev/reference/react/useTransition). It lets you start rendering "in background" with a different state value. But if you get interrupted, the renders have the current value. This highlights that in React, the same piece of state can conceptually be thought of having more than a single value (kind of like being in parallel worlds). Classes don't model that well.


I'm all for better solutions for functional components. I hope we go down that road instead of locking in the decision to rely on `class` with things like class decorators and so on.

Several React libraries are already doing that. In spite of all our warnings, people are trying to extend classes for React views, and use `class` in the rest of their code, as well, moving it into the state layer, and modeling their business domains with them instead of using pure functions and Redux.

`class` affords `extends` like balls afford throwing and chairs afford sitting. When you ignore the power of suggestion that comes with the tool affordances, you point users in the direction of trouble.

Sadly, scaling developer education is a lot harder than it seems from inside our ivory towers, surrounded by smart colleagues well-versed in programming wisdom and design patterns.


There’s a pattern I’ve seen in a lot of class components that doesn’t fit as well in a functional component: every time the component needs to render some new bit of JSX, create renderThing(), renderOtherThing(), etc, ad infinitum. These just don’t happen in functional components. Technically, you could create an inner function, but people just don’t seem to do it as much. They create a new component. Or at least, in my team, the resistance to breaking that stuff out to separate small components was much lower than with class components. It took very little effort in code reviews to push people towards small components and do it consistently.

This seems pretty disingenuous to me. React didn’t create the problem of class methods not binding this correctly. That’s purely a javascript problem, which react now provides a way to solve.

To me it's all about intent. By using a function instead of a class, you are basically saying "future reader, this component is nothing more than a simple mapping from those values to that DOM tree, don't try to look for internal states or any complex treatment here".

Sure you could write a "normal React component without state" and do the same, but having those properties baked in the way you defined the component is much stronger.


Because it's really _not_ "the most common use case". Components often pass down callbacks through multiple levels of children. A basic example would be a Form component that renders something like:

    <Button onClick={this.onFirstNameChanged} />
The proper context for `this` is the Form, but the callback is used in the context of the Button.

Besides, one of the main tenets of React is that "it's just JS", so normal JS rules apply. Now, the old-style `React.createClass()` method _did_ actually bind all "class methods" to the component instances so you didn't have to worry about it, but that wasn't typical JS behavior. ES6 classes don't auto-bind their methods, so neither does the `React.Component` base class.

The current recommended approach is to use the Stage 3 Class Properties syntax, which allows defining class methods as arrow functions that are auto-bound to the proper class/component instance. Very long-term, the React team intends to introduce a "stateful functional component" API that doesn't have to worry about class instances, but that's going to be a while. (There are some assorted userland experiments with writing "`this`-less components" as well.)


Class components still work in React, right?

Class instances are objects and it's the objects that have state. Functions can only abuse themselves as objects to store state.

function johnson() { johnson.state = {}; }

This is however shared state and the state will be reset whenever the function is called a second time. So what you normally do with a function is to pass the state via arguments and now the same function can work with different states. React could totally do this via props or as a second argument.

function Johnson(props, state) {}

Instead they changed the laws of functions so that a function can act different the first time it is called and also be called the first time multiple times if and when the function calls a functions that looks like JavaScript but follow different rules [1].

This is weird.

[1] https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at...


React isn't really object-oriented. Components rarely pass messages to each other. Instead, the way that data flows is through function/constructor arguments. You can directly invoke a method on a component, but that's only really used as an escape hatch. It's inconvenient, and IMO, a code smell.

For the React that I write, class components are used only when there's some trivial local state that I don't want to put into Redux (e.g. button hovering that can't be done in CSS), or when I need to use component lifecycle methods.

And yes, class components do inherit from React.Component, but they specifically discourage creating your own component base classes.


I'd said that it is a non-issue. One of the main tenets in React is that your component re-renders when the prop changes. I think anyone should research what exactly change in this context means and take care of not triggering the re-render when it's not wanted. The author goes out of the way to use examples that are what I would consider bad code and not something that should ever pass a code review.

I have not dealt with many junior devs, but creating an object either by using a literal or restructuring is still creating a new object and no one should expect it to be the same. Equal perhaps, but not the same. Maybe someone should explain scope and instance lifetime in the JavaScript world instead of blaming React for these. Because there is no surprise that the component re-render when you change the prop.


I was talking about classes as a design pattern, not just react classes.

That said, I don't miss higher order components, but I do find myself reaching for renderprops sometimes. I think it's still a great pattern.


I don't get it. I thought the main appeal of functions were that they were stateless so you had immutability. We already have classes if we want state, so why is this an improvement? Functions have slightly less overhead maybe? But then why didn't the React team just work on making classes have less overhead? It seems sort of "extra".

Those are two different things.

React, in its current form, requires classes or something class-like in order to implement stateful components with lifecycles. It also does not require that you handle data immutably - you can run `this.state.someArray.push(newValue); this.setState({someArray : this.state.someArray})`, and React will re-render just fine.

However, React also pushes you in functional programming directions: render methods should be pure functions, performance optimizations with `shouldComponentUpdate` work best if you've handled data immutably, and so on. The React team also plans to investigate a "stateful functional component" API at some point after the React 16 release.

It's also worth noting that React was not the only reason why classes were added to Javascript. There were hundreds of "pseudo-class" implementations out there already (all a little bit different than the others), and there was clearly enough demand for the concept to add it to the language in a standardized way.

Meanwhile, Redux has definitely helped popularize the idea of updating data immutably. The array spread operator was already added in ES6, and the object spread operator is nearing finalization. No, the language doesn't have full-blown immutable data structures, but handling data immutably is absolutely possible. If you don't like dealing with immutable updates yourself, there's Immutable.js (as you said), or dozens of immutable update utility libraries.

Particularly in the case of Redux, use of classes for data is discouraged because of the other constraints Redux asks you to follow. Plain serializable data is needed in order for time-travel debugging and persistence scenarios to work properly.

Overall, your complaints are split across a couple things. Classes exist because some JS devs want to use them. Functional-style approaches exist because some devs want to use them. Languages like C# and C++ have been "everything and the kitchen sink" for a while now, supporting multiple different paradigms in one language - Javascript is now the same way.

next

Legal | privacy