Watch out for this rendering pitfall in React Native
Intro
So, you want to render something conditionally in React - easy, right? In React/JSX, there are several ways to write “render this component if this condition is true”. A common way relies on “short circuiting” via a logical AND operator (&&
): the component will only be rendered if the left branch of the expression evaluates to a truthy value. If the left branch of the expression is falsy, the component is never mounted/rendered and the falsy value is ignored:
<View>
{renderDialog && <Dialog />} // Dialog only rendered if renderDialog is truthy
</View>
The pitfall
If renderDialog
evaluates to false
, null
, or undefined
, everything works great! Our <Dialog>
component won’t be rendered and React will ignore the evaluated value of renderDialog
.
However, there are other possible falsy values in JavaScript- and these can wreck havoc in React Native!
Let’s imagine that <Dialog>
has a required string
prop, message
. We only want to render <Dialog>
if we have a message to show. We might write our JSX like this:
const dialogMessage = ... // some string, can be empty
return (<View>
{/* ‼️ Danger - `dialogMessage` can be empty string! */}
{dialogMessage && <Dialog message={dialogMessage}>}
</View>)
If dialogMessage
happened to be an empty string, the above will effectively be:
return (<View>{''}</View>)
In React Native, the above code will cause a dreaded red screen error - Text strings must be rendered within a <Text> component
:
Based on this error, we know that in React Native all text nodes must be children of the <Text>
component. So, the empty string dialogMessage
variable above is not a valid direct child of <View>
.
This is more restrictive than React on the web, where a DOM Text node can be a child node of just about anything.
Alternatives & Prevention
Instead of using a logical AND operator with a possibly empty string, you can instead:
- use
!!
to coerce thestring
to aboolean
- use an old fashioned ternary operator
Both are safer alternatives to &&
and are just as easy to use:
<View>
{!!renderDialog && <Dialog />} // ✅ Use `!!` to convert to boolean
{renderDialog ? <Dialog /> : null} // ✅ Use ternary
</View>
There’s also a great ESLint rule, eslint-plugin-jsx-expressions, to explore adding to your React Native projects.
This rule triggers if a string is used in a JSX expression. The rule comes with autofixes - making it painless to add to your projects. Perfect for preventing an accidental empty string outside of <Text>
in React Native!
Further reading
Kent C. Dodds has a great post on some other reasons why you should avoid &&
in your components. Give that a read to learn about some other dangers of using &&
to conditionally render.