Custom render() Methods and Components

Learn how you can increase the readability of complex conditional rendering by utilizing multiple render() methods and function components.

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.