Conditional rendering in React Native may crash your app
Tags: react, reactnative, javascript, beginners
Published: 2020-05-05 17:34:18

---
title: Conditional rendering in React Native may crash your app
published: true
date: 2020-04-26 00:00:00 UTC
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/xtlamdw92q9hgd5kzeqm.jpg
tags: react, reactnative, javascript, beginners
canonical_url: https://koprowski.it/2020/conditional-rendering-react-native-text-crash/
---

Very soon after start of creating first app every developer needs to render component in one way or another depending on props. When one start searching, the first answer is inside React documentation. The answer is “Conditional Rendering”. But after a while many of us starts facing errors in React Native (or wrong rendering in React for web) that seems to jump on occasionally to reappear some time later. This article will explain what is happening and how to prevent further mistakes.

Conditional rendering using inline If with logical `&&` (AND) operator as React docs says:

> You may embed _any expressions_ in JSX by wrapping them in curly braces. This includes the JavaScript logical `&&` operator. It can be handy for conditionally including an element — [React docs: Conditional Rendering](https://reactjs.org/docs/conditional-rendering.html#inline-if-with-logical--operator)

```jsx
{someValue && (
  <View style={styles.field}>
    <Text>{someValue}</Text>
  </View>
)}
```

This handy solution is used by many and it’s nothing new for our community to see in the code. How and why it can crash your React Native app?

When using it widely in your App without proper attention sooner or later you will see this error (or _worse scenario_ your users will see that the app crashed):

`Invariant Violation: Text strings must be rendered within a <Text> component.`

Then you see such an error in your logs and scratch your head because usually it works, it may crash for one particular data entry or after some small API change. What happened? _Hint: `someValue` type matters._

#### The array example

Another common example of javascript operator wrong usage is rendering something is array contains any elements:

```jsx
// Sooner or later this code will surprise users.
// Just wait for an empty array.
{dataEntries.length && (
  <View>
    <Text>Visible only when array is not empty</Text>
  </View>
)}
```

Above example looks fine at a first glance. Array’s `length` will be `0` which is _falsy_ thus condition is not satisfied and following component is not rendered — simple. This reasoning is _partially good_ but author may forgot about one little fact that will surprise users at some point. Let’s take a closer look.

## How logical AND `&&` operator in JavaScript works?

Let’s see the docs again:

> | Operator | Syntax | Description |
> | --- | --- | --- |
> | Logical AND (`&&`) | `expr1 && expr2` | If `expr1` can be converted to `true`, returns `expr2`; else, returns `expr1`. |
> 
> […]If a value can be converted to `true`, the value is so-called _truthy_. If a value can be converted to `false`, the value is so-called _falsy_.
> 
> Examples of expressions that can be converted to false are:
> 
> - `null`;
> - `NaN`;
> - `0`;
> - empty string (`''` [..]);
> - `undefined`.
> 
> [MDN docs: Logical operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators)

Developers love that possibility to treat variables as _falsy_. Assumption is that when your variable for some reason comes not initialised from backend or other data source you have secured the code from rendering this part of View.

It seems to be a good strategy. We don’t want to show our user nicely formatted `undefined` string. It’s better to show nothing than `null` or `NaN` as well.


Note that description of AND operator says that it returns `expr1` or `expr2`. It always returns one of inputs — not converted. Again: **it converts `expr1` to `Boolean` and evaluates result but then returns original value not the converted one.** Explained as pseudo code it should look something like this:

```javascript
if (expr1 == true) {
  return expr2
} else {
  return expr1
}
```

Basically it is the whole gotcha but let’s dive into examples. I will use `Boolean()` JavaScript function to show you how values are converted.

### String variable.

```javascript
Boolean('hello world')
// -> true

Boolean('')
// -> false

Boolean(' ')	// space
// -> true

'' && 'conditionally returned string'
// -> ''

'hello world' && 'conditionally returned string'
// -> 'conditionally returned string'
```

Empty string is _falsy_ so AND operator will return `''` because the condition is not fulfilled. Returning `''` directly into ReactNative JSX will produce error `Text strings must be rendered within a <Text> component` and **cause crash**.

### Numeric variable.

```javascript
Boolean(-1)
// -> true

Boolean(0)
// -> false

Boolean(1)
// -> true

0 && 'conditionally returned string'
// -> 0

1 && 'conditionally returned string'
// -> 'conditionally returned string'
```

Zero is _falsy_ so logical AND operator will return `0` as the condition is not met. Returning `0` into ReactNative JSX will cause crash with `Invariant Violation` error again.

### Other variable types worth mentioning.

```javascript
Boolean(null)
// -> false

Boolean(undefined)
// -> false

Boolean({})
// -> true

Boolean([]) // worth noting!
// -> true
```

From the above examples the most interesting from React Native developer’s point of view is array. Usually when we put array into conditional render we would like not to render anything if array is empty. Passing an empty array into logical expression without any preparation will mislead us. What one would need to do is to check whether `length` exists and is equal to `0`.

## Why React Native crashes?

Rendering string in React Native must be wrapped with `<Text>...</Text>` component. But when we want to hide entire component when variable is empty with conditional rendering it may return an empty string directly into JSX. For example:

```jsx
let optionalStr = ''

// [...] some logic that leaves `optionalStr` empty

{optionalStr && <Text>{optionalStr}</Text>} // crash
```

Now you know that above condition is not fulfilled therefore logical AND operator will return `optionalStr` directly into main JSX.

### What about a numeric variable?

> Normally, JavaScript expressions inserted in JSX will evaluate to a string, a React element, or a list of those things. […] — [React docs: JSX in Depth](https://reactjs.org/docs/jsx-in-depth.html#functions-as-children)

React tries to convert results of your expressions into a string, React element or array. This is why you see `Invariant Violation: Text strings must be rendered within a <Text> component` even if your variable was `Number`. It may be misleading while searching for this bug in a production code.

## Why is it hard to find React Native conditional render error?

This error is sneaky because it may take a long time before it shows up. You code may be working like a charm without any issues for months and suddenly something changes on API and type of that nullable variable changes suddenly to empty string or `0`.

Why it works with variables that are `null` or `undefined` then? It will also work for booleans. React creators make our life easier and by default such variables are ignored in a JSX tree. It is special case and it will not be rendered.

React will also not crash when you put empty array directly into JSX as arrays can render multiple elements.

```jsx
// below expressions will not crash your React Native app
<View>
  {false}
  {true}
  {null}
  {undefined}
  {[]}
</View>
```

### React for web — zero appears

Developing a website in React and not converting variable into boolean will also break things but not as much as on native platform. For web empty string or 0 will be rendered. It is normal string and those can be rendered in HTML. For empty string it is usually missed and everything works well as nothing appears on the screen. It may be spotted when one try to conditionally render numeric variable as some strange 0 appears on the site. But nothing crashes and users are not upset as much.

## How to make conditional rendering safer?

Just make sure to convert every variable into Boolean before using logical AND `&&` operator.

You can do it multiple ways:

### Double negation — `!!dangerousData`

It’s an easy quick fix that will work and some experiments says that it’s execution time is faster than `Boolean(dangerousData)`.

I do not recommend it though.

This solution’s main pitfall is a human factor. Someone in your team could think that it is pointless to do double negation as it goes from `true -> false -> true`. It may lead to “refactor” that will create potential crashes in the future as this error may not reveal itself at first. My number one principle while coding is readability.

### Classic conversion — `Boolean(dangerousData)`

This seems readable but as I mentioned above some say that it is slower in execution time so make your own research and decide if it is OK for your particular case. We can find news that in modern browsers it is optimized. You may also use some transpilers to change it before it goes to final code.

### Rethink components architecture.

Maybe you don’t need as many conditional renders in the component. Every component should be small and have simplified logic as much as it can. I have seen many overly complicated components with nested conditional renders and believe me it’s not something easy to maintain as your code grows.

#### Use Element variable

In simple components sometimes you can use trick from React documentation with `if` and variable assignment preceding `return`.

```jsx
// ...
  let message = <Text>'Hello there!'</Text>
  if (isVillain) {
    message = <Text style={styles.deepVoice}>'General React'oni!'</Text>
  }

  return <View>{message}</View>
```

#### Component is a function (if else in render)

In class components it would be — `render` method is a function.

In function, you can call `return` inside `if` statement and it will not execute further on. It will have the same result as with _Element variable_ above. We don’t need else here because when condition is satisfied execution will go on, otherwise it will be stopped on first render.

```jsx
// ...
  if (isVillain) {
    return (
      <View>
        <Text style={styles.deepVoice}>'General React'oni!'</Text>
      </View>
    )
  }

  return (
    <View>
      <Text>'Hello there!'</Text>
    </View>
  )
```

#### Conditional (ternary) operator

You can also use conditional operator (ternary expression) `condition ? passed : failed` but be aware that nesting those will destroy readability of your code. My advice is to set up[no-nested-ternary](https://eslint.org/docs/rules/no-nested-ternary) rule for ESLint otherwise your code can become this: `const thing = foo ? bar : baz === qux ? quxx : foobar;` but with lots more of code because components rises very quick in amount of letters. Multiple elements inside nested ternary operator will make render complicated and unreadable.

```jsx
// ...
  return (
    <View>
      {isVillain ? (
        <Text style={styles.deepVoice}>'General React'oni!'</Text>
      ) : (
        <Text>'Hello there!'</Text>
      )}
    </View>
  )
```

### Explaining the array example (from the introduction)

Just to remind you I was showing this example:

```jsx
{dataEntries.length && (
  <View>
    <Text>Visible only when array is not empty</Text>
  </View>
)}
```

Now you understand that what really happens in above code is returning `length` to directly into JSX. It happens when `length` is _falsy_ and it comes from logical operator implementation.

To simplify the example and make things more visible lets assume that `dataEntries.length` is `0` and following `View` with `Text` component is `<Component />`. Now we have:

```jsx
{0 && <Component />}
```

This expression returns `0` which is converted to string `'0'` and you can see it as an error in React Native or as an extra character on the web.

The quickest fix possible is to **make sure that we don’t depend on _falsy_ value but on boolean `false`**.

Here are multiple fix scenarios:

#### Double negation

```jsx
{!!dataEntries.length && <Component />}
```

#### Classic conversion

```jsx
{Boolean(dataEntries.length) && <Component />}
```

#### Inline condition

```jsx
{(dataEntries.length > 0) && <Component />}
```

#### Ternary operator

```jsx
{dataEntries.length ? <Component /> : null}
```

#### Refactor, rethink, make it safe

```jsx
let conditionalComponent = null

if(dataEntries.length > 0){
	conditionalComponent = <Component />
}
```

Do you have other way to render on specific condition? Write it on [Twitter](https://twitter.com/Koprowski_it/status/1254862220829196289) or comments under this article. Let’s talk about your observations with this problem.

{% twitter 1254862220829196289 %}