I'm making a faster alternative to DevTools. Try it here.

Cleaning up ternaries in React

To write clean React, you need to write clean JSX. One of the biggest offenders of this is the ternary expression which allows you to write an inline if statement:

<div>
{ loggedIn ? <LoggedInUI /> : <LoggedOutUI /> }
</div>

This example is clean and easy to understand, but it’s not very flexible. For instance, you’d end up with this mess if you wanted to add another condition:

{
loading
? <LoadingUI />
: (
loggedIn
? <LoggedInUI />
: <LoggedOutUI />
)
}

And in practice, we often end up breaking ternary expressions into multiple lines because we have components with children:

<div>
{
loggedIn ? (
<div>
{/* A bunch of child components */}
</div>
) : (
<div>
{/* A bunch of child components */}
</div>
)
}
</div>

In practice, the components you have inside ternaries will often need props and classNames and styles and click handlers… oh my. So once you add an else if to this conditional statement, it quickly gets harder to grok.

{loading ? (
<div>
<PlaceholderData foo={bar} />
<div className="p-6 rounded some-background-color">
{/* more child elements */}
</div>
</div>
) : loggedIn ? (
<div>
<UserData foo={bar} baz={qux} onChange={() => {/* ... */}} />
<div className="p-6 rounded">
{/* more child elements */}
</div>
</div>
) : (
<div>
<LogInModal baz={qux} onSubmit={handleLogIn} />
<div className="p-6 rounded" style={{ backgroundImage: "..." }}>
{/* more child elements */}
</div>
</div>
)}

In my opinion, any time you have a multi-line ternary, you should split it into multiple components. Take the loading/logged in/logged out example. Each of these UIs should be split into its own components: LoadingUI, LoggedInUI, and LoggedOutUI. Then, the ternary should be extracted into a fourth component that holds all the rendering logic. For instance:

// Extract the ternary into its own component.
function ConditionalUI() {
const { loading, loggedIn } = usePageStatus()

if (loading) {
return <LoadingUI />
} else if (loggedIn) {
return <LoggedInUI />
} else {
return <LoggedOutUI />
}
}
// Use this where you would've put the ternary.
<ConditionalUI />

This takes more lines of code than the ternary, but it’s so much easier to read and understand. If you come back a year later to add a fourth state, you’ll know exactly what to do; if you need to add another view with more complex logic (like “logged in as paid user” or “logged out with a certain parameter in the URL”), you can simply add to the if statement.

There’s still an elephant in the room: props. If your components rely on props from the parent, you can assemble them into an object and spread them into each of the conditional UIs:

const props = {
foo: "foo",
bar: "bar",
baz: "baz",
qux: "qux"
}

// ...

<ConditionalUI props={props} />
function Example({ props }) {
const { loading, loggedIn } = usePageStatus()

if (loading) {
return <LoadingUI {...props} />
} else if (loggedIn) {
return <LoggedInUI {...props} />
} else {
return <LoggedOutUI {...props} />
}
}

And if you need to pass props down multiple levels of components (also called “prop drilling”), you can make this even cleaner by using Context. I have a post that teaches you how to set that up here, as well as how to update context from your child components:

Updating React Context inside child components

So there you have it: a way to remove complexity from JSX. Once you embrace it, React’s props and component system make it easy to write cleaner code.