facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
229.15k stars 46.89k forks source link

Use ES6 Classes to create React components. #613

Closed jordwalke closed 10 years ago

jordwalke commented 10 years ago

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;
SanderSpies commented 10 years ago

Yes! High on my personal wishlist :-). Op 26 nov. 2013 06:41 schreef "Jordan W" notifications@github.com:

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/facebook/react/issues/613 .

jordwalke commented 10 years ago

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

bjoerge commented 10 years ago

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 commented 10 years ago

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

fdecampredon commented 10 years ago

:+1:

thSoft commented 10 years ago

+1

tel commented 10 years ago

+1

thomasboyt commented 10 years ago

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 commented 10 years ago

:+1:

basarat commented 10 years ago

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

:+1: 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 commented 10 years ago

see #1380

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

ericelliott commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

@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 commented 9 years ago

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 commented 9 years ago

@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 commented 9 years ago

RE: Stateless functions - :+1:

aseem2625 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

@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 commented 8 years ago

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 commented 8 years ago

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

sassanh commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

@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?