Custom render() Methods and Components
Learn how you can increase the readability of complex conditional rendering by utilizing multiple render() methods and function components.
We'll cover the following
Separate render()
methods
Another way to increase the readability during complex conditional rendering is to move parts from the regular render()
method to separate renderXY()
methods. The regular render()
method still forms the core of the component and decides which parts of the user interface to show to the user. Therefore, this method should not become overly complex or contain any unnecessary logic.
It is not uncommon to move parts of long and complex render()
methods into much smaller, more digestible chunks and implement these as custom class methods. If proper naming is used, this technique usually aids readability and understanding. Often these custom render()
blocks are combined with if
blocks:
class Countdown extends React.Component {
renderTimeLeft() {
// […]
}
renderTimePassed() {
// […]
}
render() {
const { currentDate, eventDate } = this.props;
if (currentDate < eventDate) {
// currentDate is before eventDate so render countdown
return this.renderTimeLeft();
}
// time is over so render how much time has passed since then
return this.renderTimePassed();
}
}
This can improve the readability of the render()
method but it also increases the complexity of the component slightly. Many people recommend moving parts of the code into their own function components instead though.
Custom components with complex conditions
Instead of using multiple render()
methods, we can create new function components. These will receive props from their parent component and then take care of displaying their own data as an independent, self-governing, and testable component.
Careful consideration should be given to what data actually needs to be passed down to the new child component(s) and how to pass this data, as the component(s) should not contain too much logic or state.
Using custom components becomes useful once the render()
method in one component has
become too complex or if the same elements are used repetitively within a component.
Let’s look at a form that consists of many similar text fields. Each of these text fields is embraced by its own paragraph and contains a label
and a type
attribute. The label
also needs to be equipped with an id
that is unique to each field that it represents.
render() {
return (
<form>
<p>
<label for="email">
Email
</label>
<br />
<input type="email" name="email" id="email" />
</p>
<p>
<label for="password">
Password
</label>
<br />
<input type="password" name="password" id="password" />
</p>
<input type="submit" value="Send" />
</form>
);
}
We only have two form fields in this example, but you will find many more fields in way more complex forms in most projects. Even in this case, it might make sense to extract recurring fields into their own components to save ourselves keystrokes.
First, create a TextField
component and then cut the recurring JSX from the form component into this one:
const TextField = ({ id, label, ...HTMLInputAttributes }) => (
<p>
<label for={id}>{label}</label>
<br />
<input {...HTMLInputAttributes} id={id} />
</p>
);
export default TextField;
This new component receives an id
which is needed to link the label
to the text input. Using
Object Rest/Spread, we add all remaining props to the input
element as attributes,
transforming the component into the following:
render() {
return (
<form>
<TextField name="email" label="Email" id="email" type="email" />
<TextField name="password" label="Password" id="password" type="password" />
<input type="submit" value="Send" />
</form>
);
}
We have just transformed long and potentially hard-to-read markup into a clean and precise render()
method which only consists of very few components. If we ever wanted to add a change that should affect all of our text fields (for example, adding a class), this can be added with ease by only changing data in the new TextField
component.
Get hands-on with 1400+ tech skills courses.