So you’ve decided to build a React component. You know what it will do, what it’s called, and where it’ll go. But when you open your editor and go to write the first line, you’re confronted with the decision between three different ways to declare it? Why three? And which one should you use?
Luckily, this decision doesn’t need to be a hindrance. In fact, with a little history and two simple rules, it’ll only take a few seconds to get on with the job of writing your component. And without further adieu, here’s the first rule:
Rule 1: If your component needs to access this
, use ES6 Classes
Stateless functions don’t have a this
object; if you need anything defined on this
other than props
, you’ll need a component class.
But this.state
and this.refs
are important parts of many useful React components. Isn’t it a little weird to have this limitation?
Why don’t stateless functional components have this
?
As you may know, JSX tags compile to plain old JavaScript objects — not HTML.
Let’s take a <div />
tag for example. Passing it to the JSX compiler will return this JavaScript:
React.createElement('div')
And as React’s documentation shows, this just returns a ReactElement
– i.e. a plain JavaScript object. The structure of this ReactElement
looks something like this:
{ type: 'div', props: {}, ref: null, key: null }
But this presents an interesting problem; if each call to your component’s render
function produces a new object, how is it that its this
object continues to contain the same state
and refs
between renders? Indeed, how is it that a plain old JavaScript object is connected to the DOM at all?
React Elements and Class Instances
Ok, I was being a little tricky above. I purposefully omitted an important step.
Remember that when you create a ReactElement
with a JSX <div />
, it isn’t connected to the DOM. It isn’t until you call ReactDOM.render
on an element that it and its children are rendered to the DOM. But creating DOM nodes is not all that ReactDOM.render
does – it also creates ReactComponent
instances.
But what are these ReactComponent
instances? Think about it this way: if a ReactElement
is an instruction to React that you need an instance of your component, a ReactComponent
is the actual component. Or to put it another way, rendering a ReactElement
lets React know that we need a ReactComponent
.
Let’s check your understanding with a quick quiz. Given the above explanation, do you know what type of object your component’s this
is? Touch or hover your mouse over the box below to check your answer:
this
is a ReactComponent
instance. Each ReactComponent
holds state
, refs
, etc.
Stateless Functional Components
But stateless functional components aren’t classes – they’re functions. They don’t have a persistent this
property to attach a ReactComponent
instance to. But while a this
object expands a component’s possibilities, it also has a cost: keeping a ReactComponent
linked up with its ReactElement
objects takes work.
By using stateless functional components, you’re signalling to React that you don’t need that ReactComponent
. From this, it is able to make a bunch of optimisations — which while currently modest, are set to grow. But even before these performance improvements materialise, there are a number of things your components won’t have to do if they’re classless. Which brings us to the second rule.
Rule 2: If your components need lifecycle methods, use ES6 classes
Without a common this
property, stateless functional components wouldn’t be able to communicate with their lifecycle methods. As a result, React doesn’t provide them. And as this enlightening comment in the React codebase shows, this can save a lot of work:
/**
* ------------------ The Life-Cycle of a Composite Component ------------------
*
* - constructor: Initialization of state. The instance is now retained.
* - componentWillMount
* - render
* - [children's constructors]
* - [children's componentWillMount and render]
* - [children's componentDidMount]
* - componentDidMount
*
* Update Phases:
* - componentWillReceiveProps (only called if parent updated)
* - shouldComponentUpdate
* - componentWillUpdate
* - render
* - [children's constructors or receive props phases]
* - componentDidUpdate
*
* - componentWillUnmount
* - [children's componentWillUnmount]
* - [children destroyed]
* - (destroyed): The instance is now blank, released by React and ready for GC.
*
* -----------------------------------------------------------------------------
*/
You may have noticed that the above two rules have something in common: they’re both about the situations in which stateless functional components can’t be used. As a result, you don’t even really need two rules; they could be merged into one:
Golden Rule: If you can use a Stateless Functional Component, then do so.
Of course, sometimes you can’t use a stateless functional component. In these cases, I recommend React.Component
over React.createClass
. And to find out why, let’s learn a little history.
A little history
As you probably already know, React didn’t have stateless functional components until quite recently. In a way, this helps to explain why you’d want to use them when possible — they wouldn’t have been added unless there was a clear reason to do so.
You also may know that React.Component
is a relatively recent addition to React. This gives you a clue as to why it’d be preferred over React.createClass
. For another clue, look at the naming of their respective locations in the React repository — modern vs. classic.
But more concretely, React.Component
has a much smaller API than React.createClass
.
A smaller, equally powerful API
Where React.createClass
has mixins, ES6 Classes are well suited to Higher Order Components. While these may feel unfamiliar at first, they’ll soon feel far more natural than the alternative. HOCs give you more power than mixins in a more intuitive package. And the fact that a HOC is just vanilla JavaScript means that you don’t have to worry about them being deprecated.
Similarly, where React.createClass
has auto-binding of function methods, Babel and ES6 Classes let you bind only the required methods. This improves performance, while again decreasing the probability of your code being deprecated.
And with that, you now have the tools you need to choose how to define your components. In summary:
- If you can use stateless functional components, do so
- Otherwise, use ES6 Classes
But while this will get you over the first hump, there are many more decisions you’ll need to make. For a start, how are you going to style your new component? Will you use inline style, CSS Modules, or maybe Pacomo? And even once you’ve got your styles sorted, do you know how you’ll store your component’s state? Will use use setState
, Redux, or maybe something else?
Now you could find this all out the hard way: with Google and trial and error. Or, you could just join my Newsletter — next month I’ll be sending out a decision chart covering styling, flux, build systems, and more. Of course, it will be exclusive to subscribers.
And to sweeten the deal, in return for your e-mail you’ll immediately receive three print-optimised PDF cheatsheets. One on React (see preview), another for ES6 and yet another for JavaScript promises. All for free!
I will send you useful articles, cheatsheets and code.
One more thing – I love hearing your questions, offers and opinions. If you have something to say, leave a comment or send me an e-mail at james@jamesknelson.com. I’m looking forward to hearing from you!
Some real great reads on your blog, keep them coming :).
AFAIK React won’t re-render if a stateless component receives same props & context. Which means “PureRenderMixin” is implied with stateless functional components.
So be careful to use this mixin when using classes. See https://gist.github.com/acdlite/453f227f5e5cbbf105f8 for ES6 & React mixins.
I’m not sure what you mean by “be careful to use this mixin when using classes”? It seems to me like a bad idea to add a shallow equals comparison to all classes unless you need the performance improvement.
See this GitHub issue on the React repository for more reading –
shouldComponentUpdate
is more meant as an escape hatch for performance improvements than something which necessarily must be implemented on every component.Great post, when you say: ” if a ReactElement is an instruction to React” didn’t you mean, ” if React.createElement is an instruction” ?
While its true that
React.createElement
is an instruction in a way, all it does is return a plain object (i.e. aReactElement
). Passing thatReactElement
toReactDOM.render
is what actually creates theReactComponent
. So, you can think of theReactElement
as the instruction which is passed toReactDOM.render
.Great read – thanks.
“if you need anything defined on `this` other than `props`, you’ll need a component class.”
`context` is also supplied to a stateless functional component in the 2nd argument.
I don’t see a reason why one should use ES2015 class components over React.createClass. Both are well suited to use with HOCs, that’s the beauty of HOCs, not of classes.
I’m not against class components, but I don’t see here a real reason which explains why I must use classes instead of React.createClass. I personally prefer the latter, because it’s just a function call and I don’t want to bring new entities into the code if there’s no real benefit of using them.
+1 – I think the justification for using ES6 Classes here needs a bit more explanation.
You could still use stateless functional components if all you want to do with “this” is to get a handle to the DOM node.
const AddTodo = ({
onAddClick
}) => {
let input;
return (
{
input = node;
}} />
{
onAddClick(input.value);
input.value = ”;
}}>
Add Todo
);
};
Example from Dan Abramovs video https://egghead.io/lessons/javascript-redux-extracting-presentational-components-addtodo-footer-filterlink
Hey James, thanks for your great article! Helped a lot seeing through the mess. As a beginner in the react world and in the modern web area I could say (I remember like 7 years ago, choosing your tools was easier as we didn’t have that many options), I am having a hard time making choices which is impacting my mood haha
Thanks again!
Thanks for the knowledge drop. Always appreciated.
Doh! The code has moved the relevant lines out of the comment your “enlightening comment” links to: https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L49-L74
Maybe pick a commit hash and link to that?
Great post!
Thanks for the Post