Use Ternaries Rather than Logical AND in JSX. Or Not?
October 24, 2021Can you guess what can be problematic with the following code example?
const SomeComponent = ({ someArrayProp }) => {return (someArrayProp.length &&someArrayProp.map((item) => {return <div>{item}</div>}))}
Take a closer look at line 3. Can you see it now? We are checking if someArrayProp
has length
. What will happen if someArrayProp
is an empty array []
? You will render an 0
.
The logical AND (&&
) operator converts the first expression (someArrayProp.length
) into the boolean. If the evaluated expression is true
, it returns the second expression; else, it returns the first expression (in our case, 0
).
Ternary operator
What can be a solution to this issue? Using a ternary operator to be more explicit what you want to render in the falsy scenario:
const SomeComponent = ({ someArrayProp }) => {return someArrayProp.length? someArrayProp.map((item) => {return <div>{item}</div>}): null}
Optional chaining
But I can see another problem, can you see it as well?
What will happen if someArrayProp
won't be passed into the SomeComponent
component? You will get this error message: Cannot read properties of undefined (reading 'length')
(Your error message may differ)
We can simply fix this with the optional chaining operator:
const SomeComponent = ({ someArrayProp }) => {return someArrayProp?.length? someArrayProp.map((item) => {return <div>{item}</div>}): null}
You may think that you can remove someArrayProp?.length
and use optional chaining before calling the map
method:
const SomeComponent = ({ someArrayProp }) => {return someArrayProp?.map((item) => {return <div>{item}</div>})}
But you will encounter a different error: SomeComponent(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
As the error message suggests, we need to either render something or if we do not want to render the component, we need to return a null
.
Nullish coalescing operator
Is there any other approach we can try here? Of course, we can leverage a nullish coalescing operator!
const SomeComponent = ({ someArrayProp }) => {return (someArrayProp?.map((item) => {return <div>{item}</div>}) ?? null)}
Keep in mind that it works only because optional chaining returned undefined
, so optional chaining returns a second expression (in our case, null
).
Usage of logical AND within if
statements
We are used to using a logical AND operator in the if
condition, e.g., if both of these values are truthy, proceed further, otherwise don't. But we need to keep in mind that if both of our expressions aren't truthy, the falsy value is returned.
0 && true // 0 (The first expression is falsy and it is returned)true && 0 // 0 (The first expression is truthy, the second expression is returned)false && true // false (The first expression is falsy and it is returned)true && false // false (The first expression is truthy, the second expression is returned)
Branching syntax
Using branching syntax (ternary operator, if
statement) may be better. It is more explicit about what we want to achieve, but also it can be more verbose.
Here is example how the same code would look like using if statement:
const SomeComponent = ({ someArrayProp }) => {let children = nullif (someArrayProp?.length) {children = someArrayProp.map((item) => {return <div>{item}</div>})}return children}
From my perspective, ternary is more readable than using the if
statement (Keep in mind that readability is subjective, and it is connected with familiarity more than you would expect.).
Conclusion
Personally, I would go with a nullish coalescing operator for our use case, as I like how concise it is. But you need to understand it properly to leverage it. Here you can read more about nullish coalescing.
I'm aware that we could solve this problem by using different approaches. Still, this article aims to elaborate more on how I would approach this and the pros and cons of the specified approach.