Conditional Rendering in React

Conditionally rendering content is a pretty common pattern in React components.

class MyComponent extends Component {
  render() {
    const { user } = this.props;
    return (
      <div>
        {user && <h1>{user.name}</h1>}
        {/* other content */}
      </div>
    );
  }
}

This can quickly get messy when you introduce multiple parmeters or large conditional content blocks

render() {
  const { user, profile, otherData } = this.props;
  const shouldDisplayProfile = user && profile && otherData;
  return (
    <div>
      {shouldDisplayProfile &&
        <div>





        </div>
      }
      {/* other content */}
    </div>
  );
}

And don’t even mention ternary operators. Nothing better than scrolling through a component and finding : null} with no context 👀

Wrapping Dependent Content

In React, our unit of reuse is usually a component so can we move this behaviour into a component?

const example = (
  <OnlyIf all={{ user, profile, otherData }}>
    only render this content if all of user, profile & otherData are defined
  </OnlyIf>
);

It turns out that this is actually a pretty simple component:

  1. determine if the all prop values are truthy
  2. only render the children if they are
interface OnlyIfProps<T> {
  all: T;
}

class OnlyIf<T> extends PureComponent<OnlyIfProps<T>> {
  render() {
    const { all, children } = this.props;

    let allValuesSpecified = true;
    Object.keys(all).forEach(
      key => (allValuesSpecified = allValuesSpecified && !!all[key])
    );

    return allValuesSpecified ? children : null;
  }
}

Lazy Rendering Content

The “ component above may be useful but there is an important scenario that is not covered: children attempting to use undefined values.

const example = (
  <OnlyIf all={{ user }}>
    <h1>{user.name}</h1> {/* <-- this will throw if user is undefined */}
  </OnlyIf>
);

In this case we shouldn’t even attempt to render the children if user is undefined. We cannot prevent it when we use the structure above but if we switch to a render props pattern then we can control when children are rendererd:

const example = (
  <OnlyIf all={{ user }}>{values => <h1>{values.user.name}</h1>}</OnlyIf>
);

We can update our component to support both scenarios by…

  1. accepting either ReactNode (the default) or (values: T) =&gt; ReactNode as our children
  2. identifying the function in render and invoking it
interface OnlyIfProps<T> {
  all: T;
  children: ReactNode | ((values: T) => ReactNode);
}

class OnlyIf<T> extends PureComponent<OnlyIfProps<T>> {
  render() {
    const { all, children } = this.props;

    let allValuesSpecified = true;
    Object.keys(all).forEach(
      key => (allValuesSpecified = allValuesSpecified && !!all[key])
    );

    if (!allValuesSpecified) return null;

    return typeof children === 'function' ? children(all) : children;
  }
}

Now we can accept either a render function or a component as the situation demands!

Is this a good idea?

So we absolutely can create “ but is it useful?

On the negative side, it is an unfamiliar component that may have unexpected effects for new developers.

On the other hand, it’s fairly self explanatory and is an improvement in general readability in my opinion.

Overall I think it’s a nice addition. It certainly beats the hell out of a 20 line ternary expression!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s