DEV Community

Discussion on: A Clean Way to Conditionally Render Components

 
lukaselmer profile image
Lukas Elmer • Edited

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
 
loucyx profile image
Lou Cyx

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
Anxiny

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
 
loucyx profile image
Lou Cyx • Edited

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
Anxiny

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

Thread Thread
 
loucyx profile image
Lou Cyx

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
Anxiny

Yes, a good example of how it works

Thread Thread
 
loucyx profile image
Lou Cyx

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.