DEV Community

Heiker
Heiker

Posted on • Edited on

Creating React components using only ES5 features

This post will be just a friendly reminder that React "It's just javascript". It's okay to use it without a build step. It's okay to use it to create a tiny interactive component. And you don't have to use ES6+ if you don't want to.

Quick recap on the createElement function

React exposes a function called createElement which is the bread and butter of every component. JSX is actually syntactic sugar that translates to React.createElement calls. It looks something like this:

React.createElement(
    'tagName',       // HTML tag name or a custom component 
    props,           // HTML attributes, custom properties, events, etc...
    children         // a string or a list of components or almost anything
);
Enter fullscreen mode Exit fullscreen mode

Stateless components

This types of component only take props and returns a React element. They don't manage their own state and don't have lifecycle methods. They're perfect to present data that comes from other sources. Without any build step it could look like this:

function Welcome(props) {
    return React.createElement('div', null, 'Hello, ' + props.name);
}

ReactDOM.render(Welcome({ name: 'world' }), container);
Enter fullscreen mode Exit fullscreen mode

Class components

These components can manage their own internal state and have lifecycle methods. Here is the thing, in ES5 we can't use the class keyword to create a component, instead will have to mimic that behavior.

The first thing we will need to take care of is the inheritance. Normally you would use class MyComponent extends React.Component to do that. We're going to take another road, we will "force" the inheritance by overriding the prototype object of our component with the one from React.Component, like so:

function MyComponent(props) {
    React.Component.constructor.call(this);

    // ... code
}

MyComponent.prototype = Object.create(React.Component.prototype);
Enter fullscreen mode Exit fullscreen mode

What's different here from our stateless component is that we call React.Component constructor with the context of our custom component, and that we make sure that the prototype is the one from React.Component. With this in place now our component can use the lifecycle methods and the setState method.

Example code

Now with our new found knowledge we can create some components without setting up a build step, or worrying too much about browser support. Lets do a simple timer:

var container   = document.getElementById('app');

// h is short for hyperscript and it makes everything a little bit easier
var h           = React.createElement;

// This is how we inherit methods like setState from React.Component
Timer.prototype = Object.create(React.Component.prototype);

function Timer(props) {
  React.Component.constructor.call(this);
  var self = this;

  self.state = { seconds: 0 };

  self.tick = function() {
    self.setState(function(prevState) {
      return { seconds: prevState.seconds + 1 };
    });
  };

  self.componentDidMount = function() {
    self.interval = setInterval(self.tick, 1000);
  };

  self.componentWillUnmount = function() {
     clearInterval(self.interval);
  };

  self.render = function() {
    return h('div', null, 'seconds: ', self.state.seconds);
  }
}

ReactDOM.render(h(Timer), container);
Enter fullscreen mode Exit fullscreen mode

Notice here that I'm assigning the this context in a self variable to avoid using the bind method of the Function prototype. And I'm using it everywhere just for consistency sake, though I believe is only necessary in self.tick where I would lose the this context when using it in setInterval.

One last thing

One thing people don't notice at first is that the children of an element are also parameters. You can pass a component, a string, an object or a function. At the end of the day children is a prop.

Let me show you something.

Conclusion

This works, and is not that bad. This just shows you that you can use React with the good old script tags to embed it in any site. Another thing that shows is that React Component are function calls, you can do anything that you can think of. Really, you can do anything, including shooting your self in the foot so be careful.

Other resources

Todo List example
Timer example
React without a build step
React is just JavaScript


Thank you for your time. If you find this article useful and want to support my efforts, consider leaving a tip in ko-fi.com/vonheikemen.

buy me a coffee

Top comments (3)

Collapse
 
carc1n0gen profile image
Carson

Just a tip, putting the render and other functions in the constructor function isn't a great idea. If you have multiple instances of the component, those functions will be recreated each time. Better to put those in the prototype of the component. You lose the ability to do the var self = this trick though, and have to resort to function.bind.

Here's an example extending yours putting the functions on the component prototype: jsfiddle.net/carc1n0gen/pg1a47qt/5/

Collapse
 
vonheikemen profile image
Heiker • Edited

You are right. But Object.assign is not part of ES5, you'll have to polyfill for IE.

A little bind utility and a container object for methods could help. The constructor could look like this.

function Counter(props) {
  React.Component.constructor.call(this);
  this.state = { count: props.start || 0 };
  bind(this, methods);
}

var methods = {
 // stuff...
};

Codepen example

Collapse
 
carc1n0gen profile image
Carson • Edited

Ah I totally forgot Object.assign was not in es5. In that case, you'd keep your Object.create method of doing it, then for each other method you want on the prototype you would do

Component.prototype.something = function(...) {...}

Sorry for poor formatting. I'm on my phone.

Edit: updated fiddle jsfiddle.net/carc1n0gen/pg1a47qt/16/