I know I'm going to be laughed at for having misunderstood the latest React lingo and best practice. But guess, what I don't give a ...
I'm starting to like React more and more. There's a certain element of confidence about them since they only do what you ask them to do and even though there's state involved, if you do things right it feels like it's only one direction that state "flows". And events also only flow in one direction (backwards, sort of).
However, an ugly wart with React is the angle of it being hard to learn. All powerful things are hard to learn but it's certainly not made easier when there are multiple ways to do the same thing. What I'm referring to is how to write components.
Partly as a way of me learning and summorizing what I've come to understand and partly to jot it down so others can be helped by the same summary. Others who are in a similar situation as I am with learning React.
The default Component Class
This is what I grew up learning. This is code you most likely start with and then realize, there is no need for state here.
class Button extends React.Component {
static propTypes = {
day: PropTypes.string.isRequired,
increment: PropTypes.func.isRequired,
}
render() {
return (
<div>
<button onClick={this.props.increment}>Today is {this.props.day}</button>
</div>
)
}
}
The old style createClass
component
I believe this is what you used before you had ES6
so readily available. And I heard a rumor from Facebook that this is going to be deprecated. Strange rumor considering that createClass
is still used in the main documentation.
const Button = React.createClass({
propTypes: {
day: PropTypes.string.isRequired,
increment: PropTypes.func.isRequired,
},
render: function() {
return (
<div>
<button onClick={this.props.increment}>Today is {this.props.day}</button>
</div>
)
}
})
The Stateless Function component
Makes it possible to do some JavaScript right there before the return
const Button = ({
day,
increment
}) => {
return (
<div>
<button onClick={increment}>Today is {day}</button>
</div>
)
}
Button.propTypes = {
day: PropTypes.string.isRequired,
increment: PropTypes.func.isRequired,
}
The Presentational Component
An ES6 shortcut trick whereby you express a onliner lambda function as if it's got a body of its own.
const Button = ({
day,
increment
}) => (
<div>
<button onClick={increment}>Today is {day}</button>
</div>
)
Button.propTypes = {
day: PropTypes.string.isRequired,
increment: PropTypes.func.isRequired,
}
Some thoughts and reactions
-
The advantage with the class is that you can write a
shouldComponentUpdate
hook method. That's applicable when you have an intimate knowledge of the props and state and you might know that deep inside the props or the state, there's differences you don't need to consider different enough to warrent a re-render of the component. Arguably, if you're in that rabbit hole and need some optimization hack like that, perhaps it's time to break things up. -
The Stateless Function pattern and the Presentational Component are both functions. Here's what they look like converted to ES5: One and Two. Basically no difference.
-
The Stateless Function and the Presentational Component both suffer from the ugliness of that
propTypes
guard hanging outside the code. That makes it the opposite of encapsulated/bundled. You have to remember to copy two things. -
A lot of smarter-than-me-people seem to indicate that classes in JavaScript is a bad thing and I haven't personally understood that argument yet. What I do know is that I kinda like the bundling. You have the whole component in a little package under one name and inside you can put little helper functions/methods that support the render function. Also, having a state in one of those classes is optional. Just because a component doesn't need state, doesn't mean you have to use a functional component. Also, the class is great for putting in side-effects in the
componentWillMount
and cancel side-effects incomponentWillUnmount
. -
Supposedly with
React.createClass()
you can use mixins, but I've never used that. Is mixins something that's rapidly going out of fashion? I think I need to go back and properly read Mixins Are Dead. Long Live Composition. -
Boy I wish there was only one way to do things and only one single name. In Django you used to only have view functions. Then class-based views came along and the diversion caused a lot of strain, anger and confusion. "Why should I use which?! I hate change!" was a common noise. However, JavaScript is what it is and React is newfangled stuff.
-
I kinda like the "statement" you make when you write a stateless/presentational function component. Just by seeing its signature you can tell that it won't mess with state inside. But if your needs grow over time and you realize you need a bit of state solely for that component, you have to rewrite it entirely, right?
-
I love plainly writing down the props I need as argument and not have to write
this.props.myPropthing
. Makes it easy to debug what the code does. -
Is there not a way to put that
Button.propTypes
thing in the firstReact.Component
style into the class?? UPDATE There is! Thanks Emiliano for showing me how. -
If you're prepared to remember more terminology; the class component style is called a Container Component. I like that name!
Please Please Share your thoughts and reactions and I'll try to collect it and incorporate it into this blog post.
Comments
"Is there not a way to put that Button.propTypes thing in the first React.Component style into the class??"
Yes, you can put the propTypes inside the class if you're using ES6 with Babel (but you need to enable stage-0 features)
Just add the following inside the class
static propTypes = {
day: PropTypes.string.isRequired,
increment: PropTypes.func.isRequired,
};
Thanks! I updated the blog.
Thanks for the great write-up! I very much agree with your opinion on classes vs functions for presentational components. Presentational components often need view helpers that don't really have their place in a container (formatting a date, for example), but are too long to simply inline in the render method. When I need helpers, I resort to using classes. Any idea what we're supposed to do about this with functional components?
When using Stateless Function component, how can we add defaultProps?