React conditional rendering methods

11 Min. Read
Sep 29, 2019

Preface

In React, you can create distinct components that encapsulate behavior you need. Then, you can render only some of them, depending on the state of your application. JSX is a powerful extension to JavaScript that allows us to define UI components. But it doesn’t support loops or conditional expressions directly. If you want to iterate over a list to render more than one component or implement some conditional logic, you have to use pure Javascript. You don’t have a lot of options with looping either. Most of the time, map will cover your needs.

Obviously, there is a way around it. This is where the concept of conditional rendering is brought. In a conditional render a component decides based on one or several conditions which elements it will return. For instance, it can either return a list of items or a message that says “Sorry, the list is empty”. When a component has conditional rendering, the instance of the rendered component can have different looks. There’s more than one way to use conditional expressions in React. And, as with most things in programming, some are better suited than others depending on the problem you’re trying to solve.

Types of conditional rendering methods

If/else

The easiest way to have a conditional rendering is to use an if else in React in your render method. Imagine you don’t want to render your component, because it doesn’t have the necessary props.

Let’s create a component with the following state:

1
2
3
4
5
6
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: '', inputText: '', mode:'view'};
  }
}

We’ll use one property for the saved text and another one or the text that is being edited. A third property will indicate if you are in edit or view mode.

Next, add some methods for handling input text and the save and edit events:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {text: '', inputText: '', mode:'view'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleEdit = this.handleEdit.bind(this);
  }

  handleChange(e) {
    this.setState({ inputText: e.target.value });
  }

  handleSave() {
    this.setState({text: this.state.inputText, mode: 'view'});
  }

  handleEdit() {
    this.setState({mode: 'edit'});
  }
}

Now for the render method, check the mode state property to either render an edit button or a text input and a save button, in addition to the saved text:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class App extends React.Component {
  // …
  render () {
    if(this.state.mode === 'view') {
      return (
        <div>
          <p>Text: {this.state.text}</p>
          <button onClick={this.handleEdit}>
            Edit
          </button>
        </div>
      );
    } else {
      return (
        <div>
          <p>Text: {this.state.text}</p>
            <input
              onChange={this.handleChange}
              value={this.state.inputText}
            />
          <button onClick={this.handleSave}>
            Save
          </button>
        </div>
      );
    }
}

An if/else block is the easiest way to solve the problem, but this is not a good implementation.

It works great for simple use cases and every programmer knows how it works. But there’s a lot of repetition and the render method looks crowded.

So let’s simplify it by extracting all the conditional logic to two render methods, one to render the input box and another one to render the button:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class App extends React.Component {
  // …

  renderInputField() {
    if(this.state.mode === 'view') {
      return <div></div>;
    } else {
      return (
          <p>
            <input
              onChange={this.handleChange}
              value={this.state.inputText}
            />
          </p>
      );
    }
  }

  renderButton() {
    if(this.state.mode === 'view') {
      return (
          <button onClick={this.handleEdit}>
            Edit
          </button>
      );
    } else {
      return (
          <button onClick={this.handleSave}>
            Save
          </button>
      );
    }
  }

  render () {
    return (
      <div>
        <p>Text: {this.state.text}</p>
        {this.renderInputField()}
        {this.renderButton()}
      </div>
    );
  }
}

Notice that the method renderInputField returns an empty div element when the app is in view mode.

However, this is not necessary. We can prevent this through the usage of null.

Prevent rendering with null

If you want to hide a component, you can make its render method return null, there’s no need to render an empty (and different) element as a placeholder.

One important thing to keep in mind when returning null is that even though the component doesn’t show up, its lifecycle methods are still fired.

Back to our example, change the renderInputField method to look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
renderInputField() {
    if(this.state.mode === 'view') {
      return null;
    } else {
      return (
          <p>
            <input
              onChange={this.handleChange}
              value={this.state.inputText}
            />
          </p>
      );
    }
  }

One advantage of returning null instead of an empty element is that you’ll improve a little bit the performance of your app because React won’t have to unmount the component to replace it.

Element Variables

One thing that is an inconvenience is having more than one return statement in methods.

To solve this lets use a variable to store the JSX elements and only initialize it when the condition is true:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
renderInputField() {
    let input;

    if(this.state.mode !== 'view') {
      input =
        <p>
          <input
            onChange={this.handleChange}
            value={this.state.inputText} />
        </p>;
    }

      return input;
  }

  renderButton() {
    let button;

    if(this.state.mode === 'view') {
      button =
          <button onClick={this.handleEdit}>
            Edit
          </button>;
    } else {
      button =
          <button onClick={this.handleSave}>
            Save
          </button>;
    }

    return button;
  }

The main render method is more readable this way but maybe it isn’t necessary to use if/else blocks (or something like a switch statement) and secondary render methods.

Ternary operator

Instead of using an if/else block we can use the ternary conditional operator:

Syntax: condition ? expr_if_true : expr_if_false

The operator is wrapped in curly braces optionally wrapped in parentheses to improve readability.

Let’s apply it to the example so you can see this in action. We’ll remove the renderInputField and renderButton and in the render method, let’s add a variable to know if the component is in view or edit mode:

1
2
3
4
5
6
7
8
render () {
  const view = this.state.mode === 'view';

  return (
      <div>
      </div>
  );
}

Now you can use the ternary operator to return null if the view mode is set, or the input field otherwise:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 return (
      <div>
        <p>Text: {this.state.text}</p>

        {
          view
          ? null
          : (
            <p>
              <input
                onChange={this.handleChange}
                value={this.state.inputText} />
            </p>
          )
        }

      </div>
  );

Using a ternary operator, you can declare one component to render either a save or edit button by changing its handler and label correspondingly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 return (
      <div>
        <p>Text: {this.state.text}</p>

        {
          ...
        }

        <button
          onClick={
            view
              ? this.handleEdit
              : this.handleSave
          } >
              {view ? 'Edit' : 'Save'}
        </button>

      </div>
  );

Short circuit operator

The ternary operator has a special case where it can be simplified. When you want to render either something or nothing, you can only use the && operator. Unlike the & operator, && doesn’t evaluate the right-hand side expression if just by evaluating the left-hand expression can decide the final result.

For example, if the first expression evaluates to false (false && …), it’s not necessary to evaluate the next expression because the result will always be false.

In React, you can have expressions like the following:

1
2
3
4
5
return (
    <div>
        { showHeader && <Header /> }
    </div>
);

If showHeader evaluates to true, the

component will be returned by the expression.

If showHeader evaluates to false, the

component will be ignored and an empty div will be returned.

This way, the following expression:

1
2
3
4
5
6
7
8
9
10
11
{
  view
  ? null
  : (
    <p>
      <input
        onChange={this.handleChange}
        value={this.state.inputText} />
    </p>
  )
}

can be turned into:

1
2
3
4
5
6
7
!view && (
  <p>
    <input
      onChange={this.handleChange}
      value={this.state.inputText} />
  </p>
)

Higher-order components

A higher-order component (HOC) is a function that that takes an existing component and returns a new one with some added functionality. Applied to conditional rendering, an HOC could return a different component than the one passed based on some condition:

1
2
3
4
5
6
7
8
9
function higherOrderComponent(Component) {
  return function EnhancedComponent(props) {
    if (condition) {
      return <AnotherComponent { ...props } />;
    }

    return <Component { ...props } />;
  };
}

In functional programming, the Either type is commonly used as a wrapper to return two different values.

So let’s start by defining a function that takes two arguments, another function that will return a boolean value (the result of the conditional evaluation), and the component that will be returned if that value is true.

It’s a convention to start the name of the HOC with the word with. This function will return another function that will take the original component to return a new one:

1
2
3
4
5
function withEither(conditionalRenderingFn, EitherComponent) {
    return function buildNewComponent(Component) {

    }
}

The inner functions have access to the outer functions’ parameters, so now, based on the value returned by the function conditionalRenderingFn, you either return the EitherComponent or the original Component:

1
2
3
4
5
6
7
8
9
function withEither(conditionalRenderingFn, EitherComponent) {
    return function buildNewComponent(Component) {
        return function FinalComponent(props) {
            return conditionalRenderingFn(props)
                ? <EitherComponent { ...props } />
                 : <Component { ...props } />;
        }
    }
}

This way you can create a withEditConditionalRendering HOC and with this, create a EditSaveWithConditionalRendering component:

1
2
3
const isViewConditionFn = (props) => props.mode === 'view';
const withEditContionalRendering = withEither(isViewConditionFn, EditComponent);
const EditSaveWithConditionalRendering = withEditContionalRendering(SaveComponent);

Then you can use in the render method passing all the properties needed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
render () {
    return (
      <div>
        <p>Text: {this.state.text}</p>
        <EditSaveWithConditionalRendering
               mode={this.state.mode}
               handleEdit={this.handleEdit}
               handleChange={this.handleChange}
               handleSave={this.handleSave}
               text={this.state.inputText}
             />
      </div>
    );
}