DEV Community

Anxin.Y
Anxin.Y

Posted on

A Clean Way to Conditionally Render Components

Today, I want to share a clean way to conditionally render child components while using React. It's very simple.

Live Demo:
https://codesandbox.io/s/if-component-demo-9iipl?file=/src/App.js

Before

function App(){
  // ...
  return <div>
      {someCondition?
      <SomeChildA>
        <div>Some Contents</div>
        <div>Some Contents</div>
        <div>Some Contents</div>
      </SomeChildA>
      : 
      <SomeChildB>
        <div>Some Contents</div>
        <div>Some Contents</div>
        <div>Some Contents</div>
      </SomeChildB>}
    </div>
}

Enter fullscreen mode Exit fullscreen mode

After

function App(){
  // ...
  return <div>
      <If condition={someCondition}>
        <SomeChildA>
          <div>Some Contents</div>
          <div>Some Contents</div>
          <div>Some Contents</div>
        </SomeChildA>
      </If>
      <If condition={!someCondition}>
        <SomeChildB>
          <div>Some Contents</div>
          <div>Some Contents</div>
          <If condition={someOtherCondition}>
            <NestExample/>
          </If>
          <div>Some Contents</div>
        </SomeChildB>
      </If>
    </div>
}

Enter fullscreen mode Exit fullscreen mode

<If/> Component

function If(props) {
    return props.condition ? <>{props.children}</> : null;
}
Enter fullscreen mode Exit fullscreen mode

Thanks for reading! Have a nice day!

Discussion (27)

Collapse
lukeshiru profile image
Luke Shiru

This will still render the components inside, so if those depend on the condition, you'll still get errors. Not to mention performance is worsen as well. You can verify this by adding a log to the components inside If and setting the condition to false.

Cheers!

Collapse
igor_bykov profile image
Igor Bykov • Edited on

Just out of curiosity: do you really think that evaluation of a control-flow-like one-liner might turn into a bottleneck in a react app?

I mean, I'm agree that this approach might be error prone if one works with deeply nested & all-levels fully optional objects/arrays (which isn't the most common use case in the world by the way) but do you think it may anyhow affect the rendering performance?

I mean, it seems unlikely that someone would just accidentially stitch in all DOOM engine logic re-written & adapted for JS in-between the <If></If>, isn't it?

Collapse
lukeshiru profile image
Luke Shiru • Edited on

You don't have to take my word for this (even when I did this in the past, realized it was a mistake and stopped doing it), you can just google this "If" component approach. I'm not the only one that knows this isn't ideal because you're evaluating branches of logic even when you aren't actually going into them.

Collapse
anxiny profile image
Anxin.Y Author • Edited on

Thanks for the feedback!
I'm not sure if I understand you reply correctly, but here is demo that shows the child component will not be rendered unless the condition is true.

codesandbox.io/s/if-component-demo...

export default function App() {
  const [data, setData] = useState(null);

  return (
    <div className="App">
      {data ? <Child name="A" /> : <Child name="NO DATA" />}
      <If condition={data}>
        <Child name="Has Data" />
      </If>
      <If condition={data}>
        <ChildNeedsData data={data} />
      </If>
    </div>
  );
}

function Child(props) {
  useEffect(() => {
    console.log(`Child ${props.name} is rendered`);
    return () => console.log(`Child ${props.name} is unmounted`);
  }, []);
  console.log(`Child ${props.name} is rendering`);
  return <div>Child {props.name}</div>;
}

function ChildNeedsData(props) {
  return <h4>{props.data.name}</h4>;
}

Enter fullscreen mode Exit fullscreen mode

Output

Child NO DATA is rendering 
Child NO DATA is rendered 
// No Error while running is demo
Enter fullscreen mode Exit fullscreen mode

If you change the code to this:

      <If condition={!data}>
        <ChildNeedsData data={data} />
      </If>
Enter fullscreen mode Exit fullscreen mode

You will get a error.

Collapse
lukeshiru profile image
Luke Shiru

I was talking about this ... with a ternary you don't run the code inside the "if", but with an If component you do, because the content of an If component will be evaluated. If you want to use JSX like this, you can use Solid JS which afaik allows you to do stuff like this ^_^

Thread Thread
anxiny profile image
Anxin.Y Author

Yes, the statement in <If/> will be evaluated, but I feel it is minor problem that can be avoid, for example, enclosing the statement inside the child component.

Thanks for point this out!

Thread Thread
lukaselmer profile image
Lukas Elmer • Edited on

Beside the performance issue, this can be an issue if you check in the condition a precondition for the rendering (e.g. if an object exists), and in the rendering part you depend on this check (e.g. access properties of this object).

E.g. let post: Post | undefined
Check in the condition if post !== undefined
Render post.title if the condition is true

Thread Thread
lukeshiru profile image
Luke Shiru

Nowadays you "solve" that with optional chaining, but yeah, the important thing here is that the content of If is evaluated, and that wouldn't happen with a ternary.

Thread Thread
anxiny profile image
Anxin.Y Author

Yes, it will cause an error. But it helps you write code in a more clean way. For example:

Since you will get an error by doing this way.

export default function App() {
  const [person, setData] = useState(null);

  return (
    <div className="App">
      <If condition={person}>
        <h4>{person.name}</h4>
        <p>{person.bio}</p>
        <ul>
          {person.attrs.map(({ name, value }) => {
            return <li key={name}>{value}</li>;
          })}
        </ul>
      </If>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

It forced you to do things like this which results a cleaner code:

export default function App() {
  const [person, setData] = useState(null);

  return (
    <div className="App">
      <If condition={person}>
        <PersonCard person={person} />
        <OtherThingThatUsePerson person={person}/>
      </If>
    </div>
  );
}

function PersonCard({ person }) {
  return (
    <div>
      <h4>{person.name}</h4>
      <p>{person.bio}</p>
      <ul>
        {person.attrs.map(({ name, value }) => {
          return <li key={name}>{value}</li>;
        })}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Also, you can always check each property, but that is not recommended

<If condition={person.name}>...</If>
Enter fullscreen mode Exit fullscreen mode
Thread Thread
lukeshiru profile image
Luke Shiru • Edited on

Instead of having a component, you could have an util:

const when = (condition, render) =>
    condition ? render(condition) : undefined;
Enter fullscreen mode Exit fullscreen mode

And then you use it like this:

<div>
    {when(title, () => (
        <h1>{title}</h1>
    )}
</div>
Enter fullscreen mode Exit fullscreen mode

Behaves similarly to your If component, but it only calls render if condition is truthy, so there's a lot of issues you wouldn't have :D

Thread Thread
anxiny profile image
Anxin.Y Author

I just don't like the {...} things inside my JSX, it just doesn't look nice in terms of format.

Thread Thread
lukeshiru profile image
Luke Shiru

JSX will always end up with {...} somewhere. You can always use stuff like Svelte, that already includes "if" in their templating ☺️

Thread Thread
hvolschenk profile image
Hendrik Volschenk

I wanted to add the same comment about the content of the <If> being evaluated immediately
and got some quite surprising results.

See here: codesandbox.io/s/if-component-b5zxk

Thread Thread
anxiny profile image
Anxin.Y Author

Yes, a good example of how it works

Thread Thread
lukeshiru profile image
Luke Shiru

If you understand the underlying code for React, then this is no surprise. When you write:

import { Fragment } from "react";

const If = ({ children, condition }) =>
    condition ? <Fragment>{children}</Fragment> : undefined;

const DisplayData = ({ data }) => <Fragment>{data}</Fragment>;

const data = undefined;

export default (
    <If condition={data}>
        <DisplayData data={data} />
    </If>
);
Enter fullscreen mode Exit fullscreen mode

You're actually writing this:

import { jsx } from "react/jsx-runtime";
import { Fragment } from "react";

const If = ({ children, condition }) =>
    condition ? jsx(Fragment, { children }) : undefined;

const DisplayData = ({ data }) => jsx(Fragment, { children: data });

const data = undefined;

// The important bit:
export default jsx(If, {
    condition: data,
    children: jsx(DisplayData, { data }),
});
Enter fullscreen mode Exit fullscreen mode

Compared to doing it with a ternary like this:

import { Fragment } from "react";

const DisplayData = ({ data }) => <Fragment>{data}</Fragment>;

const data = undefined;

export default data ? <DisplayData data={data} /> : undefined;
Enter fullscreen mode Exit fullscreen mode

That's like writing this:

import { jsx } from "react/jsx-runtime";
import { Fragment } from "react";

const DisplayData = ({ data }) => jsx(Fragment, { children: data });

const data = undefined;

// The important bit:
export default data ? jsx(DisplayData, { data }) : undefined;
Enter fullscreen mode Exit fullscreen mode

With If you always call jsx with DisplayData passing data, without If you only do that call if data exists, if not you don't do anything.

Collapse
anxiny profile image
Anxin.Y Author • Edited on

If you mean the render of <If/> component will be executed regardless of the condition, that is true. But I don't think it will cause any error itself.

For performance, so far I don't feel any drawback of using this approach since it does not render its children when condition if false as I mentioned previously.
It should be very similar to the <Route/> in React-Router.

Best

Collapse
cyrstron profile image
cyrstron • Edited on

IMO, the best way of doing that is this pattern

    condition && (
        <Component />
    )
Enter fullscreen mode Exit fullscreen mode

Much more concise, decently readable and short circuit saves some resources and possible runtime errors with not evaluating jsx.

Collapse
martinpham profile image
Martin Pham • Edited on

Your idea is nice. However I think having an component would be overkill. Since the embed conditional rendering is already good.

I’d like to add a bit. Since you want to render the ComponentX only with some conditions, it’d be better if you could lazy load it. So it will be downloaded only when we need, therefore downsize the initial bundle. Lazy and Suspense can help you to do it.

Collapse
thumbone profile image
Bernd Wechner

Forgive the total newb question but what is going on here? I have never seen naked HTML used like that in a Javascript function. What's going on there?

Collapse
anxiny profile image
Anxin.Y Author • Edited on

The function here is used in ReactJS environment.
It's a functional component in React.
This format is called JSX.

Best

Collapse
thumbone profile image
Bernd Wechner

Thanks for clarifying. I'd make sure to mention that in the article but perhaps the series is in that context and I just surfed in at a single chapter out of context. Not an unusual use case though so I'd aim to include a very brief intro in each chapter to make a newcomer aware of context I guess.

Thread Thread
anxiny profile image
Anxin.Y Author

Thanks for point that out!
I will edit the post to add that.

Best

Thread Thread
thumbone profile image
Bernd Wechner

All good. Nice article. Alas I don't use React hence clueless on that syntax ;-). But a learning all the same. That's why dev.to is so nice.

Collapse
hvolschenk profile image
Hendrik Volschenk

I agree with @lukeshiru - This solution is really expensive. If you want to do this I would highly suggest you use a render prop.

Collapse
anxiny profile image
Anxin.Y Author • Edited on

Thanks for the feedback.
I don't think is good idea to put heavy task in the render return anyway so the performance does not suffer much by having a If wrappers.

If you have a case that may cause performance issue, please let me know

Collapse
kwanelegamedze profile image
Kwånelə

thanks. this opened my eyes

Collapse
mooreri63560459 profile image
moorerichard

We are a top-notch IGO Platform Development Company offering the industry best IGO development services for game developers and gaming enthusiasts to build community.