Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ES6 Classes to create React components. #613

Closed
jordwalke opened this issue Nov 26, 2013 · 30 comments
Closed

Use ES6 Classes to create React components. #613

jordwalke opened this issue Nov 26, 2013 · 30 comments

Comments

@jordwalke
Copy link
Contributor

Let's use ES6 classes to create React component classes. We've accumulated some custom concepts that don't lend themselves to using ES6 classes but we can still use them in conjunction with React components as ES6 classes, if the separation is performed as "enhancers" on top of completely pure ES6 classes:

class Typeahead extends ReactComponent {
   render() {

   }
 }

 // Enhancers
 ReactComponent
 ReactComponent.autoBindMethods(Typeahead);
 ReactComponent.validatePropTypes(Typeahead);
 ReactComponent.applyMixins([MixinOne, MixinTwo]);

 module.exports = Typeahead;
  • Blockers:
    • Probably want to call the base class ReactComponent even though the base class of our components today is called ReactCompositeComponent. We just need to rename ReactCompositeComponent to ReactComponent and ReactComponent to ReactComponentBase.
    • Right now React.createClass doesn't return the constructor. It returns the "convenience constructor" which can be invoked without new. We can unify the two concepts and eliminate convenience constructors altogether.
    • Determine how to support autobinding (likely an "enhancer" as shown above).
    • Get rid or (or figure out way to abstract out) special proprietary handling of overridden methods like componentWillMount, componentWillReceiveProps - there aren't classical OO equivalents.
    • Of these proprietary handlers, some are for the purpose of validation (preventing people from overriding base class methods). Others add actual functionality. We can do whatever we have to to ensure validation, but the additional functionality should be factored out of the class hierarchy, into special "enhancers" as I've hinted at at the bottom of the previous example. It should be possible to program with pure, straight up ES6 classes.
    • The main challenge with our proprietary handlers is how we allow multiple mixins to redefine properties, and ReactCompositeComponent will attempt to intelligently merge their results. It is okay to factor all of that out into helper utilities, and we can supply a code mod that automatically updates your code that uses mixins.
  • This is a huge undertaking. Before anyone takes a shot at this - please lock down the API. For ES6 related questions, run ideas by @jeffmo and @sebmarkbage who understand the direction of ES6. I'm happy to chat about the feasibility of potential changes to the React core, and practical ways to get started.
  • This might require tiny changes to the JSX transformer - but I'm sure @jeffmo could get these done (or help) in minutes - they're quite easy.
@SanderSpies
Copy link
Contributor

Yes! High on my personal wishlist :-).
Op 26 nov. 2013 06:41 schreef "Jordan W" [email protected]:

Let's use ES6 classes to create React component classes. We've accumulated
some custom concepts that don't lend themselves to using ES6 classes but we
can still use them in conjunction with React components as ES6 classes, if
the separation is performed as "enhancers" on top of completely pure ES6
classes:

class Typeahead extends ReactComponent {
render() {

}
}

// Enhancers
ReactComponent
ReactComponent.autoBindMethods(Typeahead);
ReactComponent.validatePropTypes(Typeahead);

module.exports = Typeahead;

Blockers:
- Probably want to call the base class ReactComponent even though the
base class of our components today is called ReactCompositeComponent.
We just need to rename ReactCompositeComponent to ReactComponentand
ReactComponent to ReactComponentBase.
- Right now React.createClass doesn't return the constructor. It
returns the "convenience constructor" which can be invoked without
new. We can unify the two concepts and eliminate convenience
constructors altogether.
- Determine how to support autobinding (likely an "enhancer" as
shown above).
- Get rid or (or figure out way to abstract out) special
proprietary handling of overridden methods like componentWillMount,
componentWillReceiveProps - there aren't classical OO equivalents.
- Of these proprietary handlers, some are for the purpose of
validation (preventing people from overriding base class methods). Others
add actual functionality. We can do whatever we have to to ensure
validation, but the additional functionality should be factored out of the
class hierarchy, into special "enhancers" as I've hinted at at the bottom
of the previous example. It should be possible to program with pure,
straight up ES6 classes.
- The main challenge with our proprietary handlers is how we allow
multiple mixins to redefine properties, and ReactCompositeComponentwill attempt to intelligently merge their results. It is okay to factor all
of that out into helper utilities, and we can supply a code mod that
automatically updates your code that uses mixins.
-

This is a huge undertaking. Before anyone takes a shot at this -
please lock down the API. For ES6 related questions, run ideas by
@jeffmo https://github.com/jeffmo and @sebmarkbagehttps://github.com/sebmarkbagewho understand the direction of ES6. I'm happy to chat about the
feasibility of potential changes to the React core, and practical ways to
get started.


Reply to this email directly or view it on GitHubhttps://github.com//issues/613
.

@jordwalke
Copy link
Contributor Author

Just a heads up about some of the concerns regarding ES6 class support and how can can address them:

  • Many people have become accustomed to using OO inheritance not just as a tool, but as the primary means of abstraction in their application. I've you've worked at a Java shop, you'll know what I'm talking about. In my personal opinion, classical OO inheritance (as implemented in many popular languages) is not often the best tool for most jobs, let alone all jobs. But the situation should be approached with even more caution when inheritance is used within a framework or paradigm that uses functional composition as its primary abstraction (React). There are certain patterns we'll want to prevent (there are many strange things that people can come up with when combining render with inheritance that don't make sense and are addressed via simple composition). There's also risk of making mutation more convenient. It might make sense to start with ES6 classes simply as a better syntax for React component creation, intentionally limiting some use cases (limiting inheritance depth, making some React base class methods final) (only when used with React components of course - all non-React use of ES6 classes wouldn't be restricted).

@bjoerge
Copy link

bjoerge commented Jan 24, 2014

FWIW I've created a simple proof of concept using Traceur to transpile from ES6 class syntax to ES5: https://github.com/bjoerge/react-es6-class/

Passing source code with ES6 class syntax through React.transform seems to work pretty well (at least in this rather limited example), probably due to Esprima's experimental support for ES6.

@panesofglass
Copy link

👍 since this will help work with other compile-to-javascript languages and frameworks such as typescript and F# (FunScript and WebSharper).

@fdecampredon
Copy link

👍

2 similar comments
@thSoft
Copy link

thSoft commented Feb 6, 2014

+1

@tel
Copy link

tel commented Feb 19, 2014

+1

@thomasboyt
Copy link

One way to handle non-method properties of classes (mixins, propTypes, etc.) would be to use the speculative annotations that Angular is planning to use: https://docs.google.com/a/venmo.com/document/d/1uhs-a41dp2z0NLs-QiXYY-rqLGhgjmTf4iwBad2myzY/edit#heading=h.ambwele793xv

You then might end up w/ something like:

import {ReactComponent} from "react/ReactComponent";
import {WithMixins, PropValidate} from "react/util";
import props from "react/props";
import MyMixin from "app/components/MyMixin";

var props = {
  myProp: props.fn.isRequired
};

@WithMixins(MyMixin)
@PropValidate(props)
class Component extends ReactComponent {
  // ...
}

Of course, these won't actually be in ES6 (but are on the table for ES7), so you'd be introducing a required build step for this syntax (whether with Traceur's version or a sweetjs macro, etc). Devs who would like to opt-out of that step could either stick to React.createClass(), or maybe use the syntax the original snippet showed, e.g.:

class Component extends ReactComponent {
  // ...
}

Component.addMixins(MyMixin);
Component.propValidate(props);

@magalhas
Copy link

👍

@basarat
Copy link
Contributor

basarat commented Apr 10, 2014

We can unify the two concepts and eliminate convenience constructors altogether.

👍 people can be trusted with new at this point.

special proprietary handling of overridden methods like componentWillMount, componentWillReceiveProps - there aren't classical OO equivalents.

super should do the trick :

class ReactComponent{
    componentWillMount(){
        console.log('base implementation');
    }
}

class Typeahead extends ReactComponent{
    componentWillMount(){
        super.componentWillMount();

        // Additional implementation
    }
}

or perhaps I am missing something.

@sebmarkbage
Copy link
Collaborator

see #1380

I'll close out this issue since most of the info here is stale.

@ericelliott
Copy link
Contributor

Please don't use ES6 class or class inheritance at all, anywhere.

Specifically in the context of React, you'll be completely missing the point of reactive programming.

Super is a code-smell anywhere, but it's particularly abhorrent in reactive programming.

"Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function." ~ John Carmack

@bjrmatos
Copy link
Contributor

bjrmatos commented Mar 1, 2015

I agree with @ericelliott, declare my components with classes feels so weird, using React.createClass feels sugar, the internals are abstracted in this function.

Maybe i'm missing something but are there good benefits for use ES6 classes in React?

At least i hope you dont deprecate createClass in the future

@jordwalke
Copy link
Contributor Author

I agree that OO concepts such as super and dynamic dispatch make code more difficult to reason about. Using JS ES6 classes could still be used just to group related functions together that operate on an expected form of data without needing to allocate separate instances of those functions per instance (using JS's prototype - (which comes with all the dynamic dispatch downsides)). Accomplishing the same in JavaScript without in some way using prototype would be difficult. Even in the pre-ES6 React form, React was still using prototypes, but we managed to ensure that people didn't abuse them. I believe it's possible to do the same with ES6 classes.

@ericelliott
Copy link
Contributor

Export a factory instead of a class (like React does today), and you'll help users avoid a jungle full of pitfalls and gorillas.

@sebmarkbage
Copy link
Collaborator

@ericelliott Note that classic React factories created through createClass are just OOP classes like ES6 classes. They support the same kind of inheritance through mixins. In fact, because it also supports multi-inheritance it opens up even more pitfalls. In fact, mixins is unfortunately seen as the primary way to do abstractions.

ES6 classes in React is not adding anything you couldn't already do. In fact it is constraining it further by encouraging object composition instead of mixins. It is an unfortunate marketing effect that this move is seen as encouraging OOP when it is really not.

My stance on progress in this space is that you can't take things away from developers until you've taught them the alternatives... at scale. (That includes myself.)

The class system provides an optional escape hatch when you need it rather than completely stopping you.

The primary feature that our class system provides is an "instance" handle this has several features.

  1. It provides a certain level of familiarity and convenience. You can use this as a middle man to refer to a group of arguments. This is a foot-gun but makes it easier to onboard new people.

  2. The instance is an ID that you can use to refer to a place in the tree. It allows APIs like React.findDOMNode(component) and third-party APIs that can unify around it.

  3. It provides single or multiple inheritance features if someone needs to create an abstraction and just can't figure out how to do it using composition. This is unfortunately a very common problem.

If a developer can't figure out a way to do it, we don't want them to get stuck. Therefore, OOP is an escape hatch. At the same time we're trying to teach and encourage composition of components and higher order functions/components instead of OOP. You can still implement that on top of the class systems that we have. Then, when this practice is common enough, we can start deprecating old class systems.

However, we make that progress by teaching and encouragement - not by force.

As a phase two of this, we can start introducing more pure models. See the alternatives that we've been working on to replace "instances" as an abstraction model:

https://github.com/reactjs/react-future/blob/master/01%20-%20Core/03%20-%20Stateless%20Functions.js
https://github.com/reactjs/react-future/tree/master/07%20-%20Returning%20State

As well as a declarative ways of updating state:

https://github.com/reactjs/react-future/tree/master/09%20-%20Reduce%20State

@samwgoldman
Copy link
Member

The stateless function example you linked is exactly how I want to write components! Ideally with PureRenderMixing behavior built in. Looking forward to that future.

@ericelliott
Copy link
Contributor

@sebmarkbage I'm a little hazy on why you need class to do any of that. Can you point me to examples of the instance handle use?

RE: provide inheritance options, see Prototypal Inheritance with Stamps.

There's currently a project underway to make stamps produced by Stampit immutable, as well.

Re: pitfalls -- In my experience, single inheritance has many more pitfalls than Object.assign style mixins.

@ericelliott
Copy link
Contributor

RE: Stateless functions - 👍

@aseem2625
Copy link

Hi,
I referred to link https://github.com/reactjs/react-future/blob/master/01%20-%20Core/03%20-%20Stateless%20Functions.js

Is prop validation needed for stateless components. Or it's meaningless(and wrong) to put prop validation as these stateless components are merely the functions and for concept sake called " react's stateless component" ?

@gaearon
Copy link
Collaborator

gaearon commented Dec 26, 2015

Is prop validation needed for stateless components

It's never required for any components, but unless you're already using Flow, we suggest you to put propTypes on all components. It's not different for functional components:

const MenuItem = ({ title }) => <div>{title}</div>;
MenuItem.propTypes = {
  title: React.PropTypes.string
};

@aseem2625
Copy link

@gaearon Thanks. I was wondering that these stateless functions(components) looks simply like functions so how come React treats them as components!! And also, am I correct in saying that these stateless functions don't have life cycle methods like DidComponentMount, etc as they don't extend 'React.Component'. Because presence/absence of state can't be the only difference between usual components and stateless function components.

@gaearon
Copy link
Collaborator

gaearon commented Dec 28, 2015

I was wondering that these stateless functions(components) looks simply like functions so how come React treats them as components!!

Starting from 0.14, React allows components to be declared as functions. This is useful for simple components that have no lifecycle methods or state, and in the future React might apply certain optimizations to them (but not today).

Please read the announcement from 0.14 release notes.

@aseem2625
Copy link

@gaearon Thanks. Now I know that jsx files having simple functions are taken as components by React.
Thanks that helped.

@sassanh
Copy link
Contributor

sassanh commented Jul 24, 2016

Well reading above comments I don't know how to tell this. I know you're just against using oop in react applications. Lets forget about oop and go with (quoting @jordwalke) "ES6 classes simply as a better syntax for React component creation". Just to make the syntax better is it possible to support inheritance? I think it'd be much cleaner syntax than wrappers and mixins.

@ericelliott
Copy link
Contributor

Just to make the syntax better is it possible to support inheritance? I think it'd be much cleaner syntax than wrappers and mixins.

This is the kind of thinking you get when you use class, because class affords extends like balls afford throwing and chairs afford sitting, and this is where that thinking leads.

@sassanh
Copy link
Contributor

sassanh commented Jul 25, 2016

I can't believe that somebody actually written that nonsense and I can't believe that it's being referenced. If he can't use/doesn't know how to use oop paradigms doesn't mean he should write something titled "Goodbye, Object Oriented Programming".

The kind of thinking you mentioned is the kind of thinking that has led to the browser you're using, operating system you're using, and more than one third of all software has written so far. That kind of thinking which is "to reuse" has its roots in modernism and even the car you drive, the hardware you run your software on, the airplane you travel with, the phone you have in your pocket are using it.

Good for the human being that it has reached to a level that class affords extends for it like balls afford throwing.

@sebmarkbage
Copy link
Collaborator

This thread is not about the validity or success of OOP. React classes already support inheritance if you want it, we just don't recommend it after our experience with it.

There's nothing technical to address here so I'd recommend taking the philosophical discussion to Medium, Twitter or a nice Facebook group.

@sassanh
Copy link
Contributor

sassanh commented Jul 25, 2016

I'm sorry that discussion happened here.

It'd be great if you support inheritance, currently it works but as you know there are some issues with it. Is it possible to plan to solve these issues and support inheritance officially if it's not taking too much effort? It'd result in much cleaner code if nothing else.

@ericelliott
Copy link
Contributor

@sebmarkbage My apologies. I'm hopeful that we move toward more functional components in the future so that lots of React users don't have to relearn inheritance pitfalls the hard way.

For those who like classes and want to make the best use of them with React, @gaearon wrote an excellent guide, "How to Use Classes and Sleep at Night".

Perhaps it would be useful to mention inheritance and some of Dan's recommendations in the documentation to help guide people down paths toward success (HOC) and help them avoid some pitfalls?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests