Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at the New York ColdFusion User Group (Jul. 2008) with: Simon Free and Peter Bell and Dan Wilson
Ben Nadel at the New York ColdFusion User Group (Jul. 2008) with: Simon Free@simonfree ) , Peter Bell@peterbell ) , and Dan Wilson@DanWilson )

Extending React.Component Using ES5 With ReactJS 0.14

By Ben Nadel on

In my last post on rendering large datasets with Angular 2 Beta 3 and ReactJS, I used ReactJS 0.14 for the first time. Along with the library upgrade, I thought I would also try to change my approach to creating React components: instead of using React.createClass(), like I used to, I thought I would try explicitly extending the React.Component class. The ReactJS documentation uses ES6 and the "extends" operator. But, because I think prototypal inheritance is super interesting, I wanted to demonstrate how such a class extension works in an ES5 context for anyone who might be curious.

OOPS: In the video, I kept saying ".createComponent()" when I meant to say ".createClass()". Sorry for the confusion.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

JavaScript doesn't have classes in the same way that Java has classes. As you might know, it uses prototypal inheritance in which each object inherits from another "living object." So, when you see "extends" in ES6, that's basically just "class sugar" on top of the existing prototypal inheritance machinery. As such, we can easily translate ES6 class-based code into ES5 prototype-based code.

For ReactJS, our stateful components need to extend the React.Component class so that it can inherit things like the .setState() method and any other wiring that React needs to supply. If we didn't need a stateful component, we could just use a "vanilla" JavaScript class. Or, we could bypass class creation altogether and just provide a render() function as "the class."

NOTE: Using a render() function as a component produces a component that neither has life-cycle methods nor can be referenced via the "refs" collection.

But, since the point of this post is to create a stateful component, we can use Object.create() in order to create a constructor whose prototype points to the React.Component prototype. Then, when we (ie, React) instantiate the component, instead of having access to something called "super", we need to consume React.Component as our super constructor.

In the following code, I've chosen to use the module pattern for my Demo component instead of defining the Demo instance methods on the constructor prototype. This is a personal preference and has pros and cons in either direction - your mileage may vary. That said, it doesn't affect the core concept one way or the other.

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Extending React.Component Using ES5 With ReactJS 0.14
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="./demo.css"></link>
  • </head>
  • <body>
  •  
  • <h1>
  • Extending React.Component Using ES5 With ReactJS 0.14
  • </h1>
  •  
  • <div id="content">
  • <!-- This content will be replaced with the React rendering. -->
  • </div>
  •  
  •  
  • <!-- Load scripts. -->
  • <script src="../../vendor/reactjs/0.14.7/react.js"></script>
  • <script src="../../vendor/reactjs/0.14.7/react-dom.min.js"></script>
  • <!--
  • CAUTION: Using the JSXTransformer for in-browser transpiling - this is deprecated
  • and not recommended with 0.14.7. But, this should only affect the initial page
  • load, not the post-rendering performance (my assumption).
  • -->
  • <script src="../../vendor/reactjs/JSXTransformer-0.13.3.js"></script>
  • <script type="text/jsx">
  •  
  •  
  • // Demo has to extend Component since it's a stateful component. In terms of
  • // prototypal inheritance this means that the component's prototype extends
  • // the React.Component prototype.
  • // --
  • // NOTE: We would not actually have to do this if we didn't need the component
  • // to be stateful. If we didn't need state, we could just use a naked render
  • // function or a plain constructor that exposed a public render function.
  • Demo.prototype = Object.create( React.Component.prototype );
  •  
  • // Configure the prop restrictions for the component.
  • Demo.propTypes = {
  • initialCount: React.PropTypes.number.isRequired
  • };
  •  
  • // I manage the demo.
  • function Demo( initialProps ) {
  •  
  • // Call the super constructor in the context of the current instance.
  • React.Component.call( this, initialProps );
  •  
  • var vm = this;
  •  
  • // Setup the initial state before the component is mounted.
  • vm.state = {
  • counter: initialProps.initialCount
  • };
  •  
  • // Expose the public methods.
  • // --
  • // NOTE: If you wanted to, you could also define these methods on the
  • // Demo prototype (after you extended the React.Component prototype).
  • // Doing so is more memory efficient and may allow the VM to optimize
  • // the use of the "class" behind the scenes. However "memory is cheap,"
  • // most apps don't require that much processing, and the chances are HIGH
  • // that you probably destroy all that "goodness" by calling .bind() on your
  • // methods _anyway_. As such, I prefer using more of a "revealing module"
  • // pattern to just make life a whole lot easier.
  • //
  • // .... your mileage may vary.
  • vm.render = render;
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I render the view using the current state and properties collections.
  • function render() {
  •  
  • return(
  • <div>
  • <p>
  • The current count is { vm.state.counter }.
  • </p>
  •  
  • <p>
  • <a onClick={ increment }>Increment that count</a> like a boss!
  • </p>
  • </div>
  • );
  •  
  • }
  •  
  •  
  • // ---
  • // PRIVATE METHODS.
  • // ---
  •  
  •  
  • // I increment the current counter.
  • function increment() {
  •  
  • vm.setState({
  • counter: ( vm.state.counter + 1 )
  • });
  •  
  • }
  •  
  • }
  •  
  •  
  • // --------------------------------------------------------------------------- //
  • // --------------------------------------------------------------------------- //
  •  
  •  
  • // Render the root Demo and mount it inside the given element.
  • ReactDOM.render( <Demo initialCount={ 5 } />, document.getElementById( "content" ) );
  •  
  • </script>
  •  
  • </body>
  • </html>

Now, with React.createClass(), you never need to bind your component methods because ReactJS does that for you automatically. When you explicitly extend React.Component, however, you don't get that benefit. That said, in my example, I have no need to use bind methods either because I am using the module pattern - one of the really nice benefits. But, again, this is a personal preference - I am not trying to say you should go this route - it's just the way I like to do things.

When we run this code, which implements a simple counter, we can successfully increment the value, calling .setState(). And all is good:


 
 
 

 
 Extending React.Component using ES5 with React 0.14.7. 
 
 
 

Of the thousands upon thousands of people out there programming in ReactJS, I would guess that only a handful of you actually care about ES5. But, even so, I think there's always something magical about seeing and ES5 example and taking a moment to think about what your "extends" operator and your "super" function is actually doing.




Reader Comments

(applause)

Always good to de-mystify some of the ES6 magic and give everyone another way of thinking about things.

Reply to this Comment

@Chris,

I'm glad you appreciate. While ES6 offers some really nice syntax improvements, I think there's a huge value-add to understanding what is happening at the ES5 level.

Reply to this Comment

I've spent the last year of my life in Angular 1.x and Python. We are now moving to React/Redux+Node, and I've been spending time making sure I understand what's going on under the covers of both React and Babel since my entire career as been spent writing ES3 and ES5 flavors of JS.

The react component was next on my list and this article gave me some of my time back. It is as simple as I expected, and I appreciate the time you took to post this.

Reply to this Comment

@Jeremy,

Very cool - glad I could help.

On a side note, how are you liking the transition from Angular to React? Is there anything specifically that you can share about the benefits of React? I'm an AngularJS developer most of the time, but like to explore React to see how other people solve problems. But, I've not really written any React in production, so my experience is mostly limited. Would be curious to hear your experience.

Reply to this Comment

@Roman,

That's a great question. I think when you use a "JavaScript Class," as opposed to using React.createClass(), you can't use Mix-ins. I think it's one or the other, but not both. From the docs:

> Unfortunately ES6 launched without any mixin support. Therefore, there
> is no support for mixins when you use React with ES6 classes. Instead,
> we're working on making it easier to support such use cases without
> resorting to mixins.

Source: https://facebook.github.io/react/docs/reusable-components.html#no-mixins

Not sure if that is going to change with 0.15 - I'm not that current on my React studies.

Reply to this Comment

@Ben,

I find writing and supporting production code to provide me a lot of insight into the pros and cons of a framework, as well as the difference between what a framework provides and what I have to write myself.

For the record, I like Angular 1.x a lot, and I feel that I have enough experience with it now to work around pitfalls and silliness. I feel like I could probably spin up a "thick client" app faster in Angular than I could in React/Redux (which is fair, since I'm comfortable in Angular and not in React/Redux).

However, what we're doing in my current company is not rocket science. While switching frameworks is always a pain, I think for my team's needs it's the right choice that we sink the cost now.

The biggest practical win we're seeing is that we went from having Python+Jinja templates on the server side mixed with Angular templates on the client side to just a single set of templates and a single templating language. When it comes to maintenance and debugging, that really is a huge win in terms of just decision making. No more, "Do I write this in Jinja? --time passes-- Damn, I should have written this in Angular."

React gives us the ability to do server side rendering with the exact same template language (JSX) as we would use on the client side. And at our company we have never been able to escape needing both (as most companies probably don't) client side rendering vs. server side rendering.

One big positive side note for Angular is that as our team experience in it grew over the last year, maybe I'm just missing something in the React/Redux model, but I find sharing components easier in Angular than I do in React/Redux. I feel that there is a very good solution, but the interfaces that Angular provides needs to be made up on a team by team basis for sharing components in React/Redux, which feels less reusable to me.

The positive of React for me of it being a very, very fast view layer, is also a negative for me in terms of the software architecture. Do you do Flux? Redux? Reflux? I do like democracy, but it really sucks for someone getting on board who needs to build a "big" application. My personal recommendation is Redux (from also having tried Flux), but I see positives already in the Flux model (the ability to have many stores) that sometimes is annoying in the Redux model (nope, you get one store only if you're doing it right).

I ramble too much. Just my insights. Love your blog, thanks for sharing as much as you do.

Reply to this Comment

@Ben,

I has been resolve this problem. For support mixins you can install npm package "react-mixin". Its correctly work with ES5.

If you want apply mixin for your class, you can made this:
var ReactMixin = require("react-mixin");
ReactMixin.onClass(<your_class>, <your_mixin_name>);

Reply to this Comment

@Jeremy,

Thanks so much for the insight (and sorry for not responding sooner). About the server-side templates, though, are you using Python to render the JSX on the server? Or did you switch over to Node? Server-side rendering of JS templates is still not something that I have a whole lot of experience with. In fact, server-side rendering in a "universal JavaScript" sense still completely confuses me (even at the philosophical level, let alone the practical nuts and bolts).

As far as sharing components, that's an interesting thought. I wonder if sharing in Angular feels easier because its is more constrained. Meaning, in AngularJS, everything has to be put into a "module" and Angular manages all of the dependency-injection. There's just not that much "wiggle room" in how you can wire things together. In React, since there is no DI container (out of the box), it's sort of up to you to figure out how to wire everything together. So, there's no clear path (enforced by constraints).

I wish I was better at architecture in general. I get so mired in the nuts and bolts of implementation and syntax and features, I never take the time to increase my skills of the overall planning.

Reply to this Comment

@Roman,

Ah, very cool - glad you found the solution - I was a bit out of my element on that one :)

Reply to this Comment

@Ben,

Don't worry about the delay responding, my life around JS is asynchronous anyway (hah, dad + programmer joke combined.)

We're only using react for new software that we're (re)writing. We are using node to "render" on the server side, along with babel + webpack to transpile the react + JS code down to ES5 friendly code.

In terms of rendering on the server side or client side, it's more of a human problem / time constraint problem than anything else. To me, I feel the best way to figure out where to build the HTML is to look at the existing infrastructure and make a decision. Unfortunately in my past, there have been times, due to APIs or existing code written by other people, that it's nigh impossible to do all rendering on the client side (a. la. Angular) and I have to have some level of html building on the server side. Often it has to do with non-robust APIs that leak too much sensitive data, and it's easier to render some HTML than transform that data into another bit of JS before shipping it down. Not what I'm asserting is the right answer of the way to do things, but having the ability to use the same template to write some HTML on the server side as on the client has proven to be useful.

Something that I noticed with my own use of angular is that it took a long time to do simple things, but for more complex things angular gives me pretty much everything I want or need for the code and UX located in the web browser. React + Redux is really cool for simple things, but for complex things, I find one needs to make choices up front. Just google search, and see how many different ways you can set up a react app.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.