Carl Rippon

Building SPAs

Carl Rippon
BlogBooksAbout
This site uses cookies. Click here to find out more

React Conditional Rendering Patterns

April 17, 2018
react

Rendering elements based on certain conditions is a common task we need to accomplish when building react screens. In JSX we have all the power of JavaScript, so, there are many options for conditional rendering. Here’s the conditional rendering the patterns I’m using at the moment for different scenarios …

React Conditional Rendering Patterns

Condition with 1 branch - short-circuit operator

If we have a condition with only a single branch then the short-circuit operator, &&, allows us to concisely express this. Basically, the right operand of && isn’t evaluated and rendered if left operand is falsy.

In the example below, the “Reset Data” button is only shown if the user is an admin.

class ConditionWith1Branch extends Component {
  state = {
    user: { name: "Fred", isAdmin: true }
  };
  render() {
    return this.state.user.isAdmin && <Button>Reset Data</Button>;  }
}

Complex condition - extract into function

If the condition is complex, we can extract the condition into a function. This keeps the render function nice and readable.

In the example below, the button is rendered only if the user is an admin or the owner of the record.

class ComplexCondition extends Component {
  state = {
    user: { name: "Fred", isAdmin: false },
    record: { owner: "Fred" }
  };
  HasPermission() {    if (this.state.user.isAdmin) {      return true;    } else {      if (this.state.user.name === this.state.record.owner) {        return true;      } else {        return false;      }    }  }  render() {
    return this.HasPermission() && <Button>Delete Data</Button>;
  }
}

Complex branches - extract into function

If a branch becomes large, then this can be extracted out into a function, making the parent render function a little more readable.

class ComplexBranch extends Component {
  state = {
    user: { name: "Fred", isAdmin: false }
  };
  renderAdminOptions(props) {    return (      <div>        <Button>Delete Data</Button>        <Button>Restore Data</Button>      </div>    );  }  render() {
    return this.state.user.isAdmin && this.renderAdminOptions();
  }
}

Condition with 2 branches - ternary operator

The short-circuit operator is great but in a lot of cases we’ll have 2 branches of logic - i.e. if … else …

The ternary operator, ?:, lets us nicely express 2 branches of rendering logic. The element immediately after the ? is what is rendered if the condition is truthy and the element after the : is what is rendered if the condition is falsy.

In the example below a “Sign Out” button is rendered if the user is signed in, otherwise a “Sign In” button is rendered.

class ConditionWith2Branches extends Component {
  state = {
    isSignedIn: true
  };
  render() {
    return this.state.isSignedIn ? (
      <Button>Sign Out</Button>
    ) : (
      <Button>Sign In</Button>
    );
  }
}

As noted in previous sections, complex conditions and branches can be extracted out into functions to keep our render function nice and readable.

Conditions with >2 branches

If we have more than 2 branches of logic we might naturally do something like below with a switch statement. The code displays a different button depending on the value of status in our state.

class MultipleBranchesWithSwitch extends Component {
  state = {
    status: "InWork"
  };
  render() {
    return (
      <div>
        {(() => {
          switch (this.state.status) {
            case "NotStarted":
              return <Button>Start</Button>;
            case "InWork":
              return <Button>Approve</Button>;
            case "Approved":
              return <Button>Complete</Button>;
            case "Completed":
              return <Button>Rework</Button>;
            default:
              return null;
          }
        })()}
      </div>
    );
  }
}

However, we can make this a little more concise by doing something like below which is the equivalent to above.

class MultipleBranches extends Component {
  state = {
    status: "InWork"
  };
  render() {
    return (
      <div>
        {
          {
            NotStarted: <Button>Start</Button>,
            InWork: <Button>Approve</Button>,
            Approved: <Button>Complete</Button>,
            Completed: <Button>Rework</Button>
          }[this.state.status]
        }
      </div>
    );
  }
}

Pretty neat!

Repeatable conditions - HOC

We can use higher order components to avoid repeating the same conditional logic. Below is a common example of displaying a loading indicator whilst isLoading is true in the components state.

function WithLoading(Component) {
  return function WithLoadingComponent({ isLoading, ...props }) {
    return (isLoading ? <p>Loading ... please wait ...</p> : <Component {...props} />)
  }
}

const MyButton = (props) => {
  return <Button>My Button</Button>
}

const MyButtonWithLoading = WithLoading(MyButton);

class App extends Component {
  state = {isLoading: true}
  render() {
    return (
        <MyButtonWithLoading isLoading={this.state.isLoading} />
      </div>
    );
  }
}

Conclusion

The short-circuit and ternary operators are a great way of concisely expressing conditional logic. Extracting complexity into functions and using HOCs for common conditional logic helps keep our render methods nice a readable.


Want to learn more about React and TypeScript? Check out my book
Learn React with TypeScript 3