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
);
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);
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);
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);
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.
Top comments (3)
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/
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.
Codepen example
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/