Don't Split render() into Functions
Published onUnlike many other frameworks, React is very liberal in terms of where to place your logic. It doesn’t tell you how you should structure your application, which is nice because there are many exceptions to the rule when it comes to frontend development.
However, this also forces us to make a lot more decisions.
Don’t worry, I won’t talk about structuring an entire web application, that would be an insanely broad subject. Instead I’ll focus only on the render()
method of React components.
All of the following is equally applicable to stateless components.
Splitting features into functions has becoming an increasingly popular pattern in JavaScript. Having descriptive function names makes code easier to read and reduces the need for documentation, which we often forget to maintain. However, the case where I think this has gone too far is splitting the render()
method into functions.
Here is a simple example:
class List extends React.Component {
renderItems(items) {
return items.map(item =>
<li key={item.id}>
{item.text}
</li>
);
}
renderLastItem(hasLastItem) {
return hasLastItem
? <li>Last</li>
: null;
}
render() {
return (
<ul>
{this.renderItems(this.props.items)}
{this.renderLastItem(this.props.hasLastItem)}
</ul>
)
}
}
Let’s say that I didn’t write this code and I want to figure out what this component does. I’ll probably start with the render()
method because a React component is usually defined by what it renders.
The first obstacle I will come across is this.renderItems
and, even though the method does tell me that it renders some kind of list items, I want know exactly what they will look like, this is why I’m reading the render()
method in the first place. Now I have to search for renderItems()
to find out what it renders, then I have to come back to render()
and mentally insert that output in the place of the this.renderItems
call. Now, the same thing all over again for renderLastItem
. 🙄
The sole purpose of render()
is to define the component’s output—what benefit does extracting that logic provide?
Consider this updated example:
class List extends React.Component {
render() {
return (
<ul>
{this.props.items.map(item =>
<li key={item.id}>
{item.text}
</li>
)}
{this.props.hasLastItem
? <li>Last</li>
: null}
</ul>
)
}
}
I find this much easier to read; top-to-bottom without interruptions.
At this point maybe you’re about to ask “but don’t (custom) components in render()
also cause a reading disruption?”
In a way, but a necessary one. When you extract something into a separate component, it’s usually for the purpose of splitting a feature into digestible/reusable parts, which is useful for testing and maintaining focus. I don’t find the pattern from my first example useful because people are usually keeping those functions in the same file (if not in the same class), so the code doesn’t become more compact, just more… scattered.
My rule of thumb
The
render()
method and stateless components are the only places where JSX should be. If the rendering logic grows too big, split it into more components.
Like with any rule, there are exceptions; you’ll know when you see them.
This is only my side of the story, I’m very eager to hear your side, dear render-splitting reader. What do you think? 😉