🗞️

React Native の JSX 内で && 演算子を使ったらエラーに怒られた

3 min read

始め

先月から React Native を使うプロジェクトに参加することになって頑張って取り組んでます。が、ウェブと違う部分で戸惑ったことがありましたので記事にしたいと思います。

1. 発端

API から持ってきたイメージの source Url をコンポーネントに使おうとしたときでした。

<View>
  {imageUrl && <Image source={{ uri: imageUrl }} />}
</View>

そうしたらなぜか以下のエラーで怒られたのです。

Invariant Violation: Text strings must be rendered within a Text component

ウェブだったら問題ないはずですが、RNではだめなようです。

2. 原因

まずは原因を探しました。

2-1. React Native

ウェブだったら文字列をdivタグとかに直接入れてもなんの問題もないです。

<p>文字列が入れます</p> // OK
<div>文字列が入れます</div> // OK
<span>文字列が入れます</span> // OK

ですが、React Native では文字列は必ずTextコンポーネントの中に入れなければならないというルールがあります。

<Text>文字列が入れます</Text>
<View>文字列は入れません</View> //Invariant Violation: Text strings must be rendered within a Text component

私が怒られたエラーと同じですね。つまり、私は文字列をTextタグに入れなかったらエラーに怒られたことになります。

それはわかりましたが、でも文字列を画面表示してるわけでもないし、Imagesource属性で使ってるだけなのになんで? と思いました。もうちょっと深ぼる必要がありそうです。

2-2. React のレンダリング

Reactの公式サイトにはこういう説明があります。

false, null, undefined, and true are valid children. They simply don’t render. These JSX expressions will all render to the same thing:

<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>

truefalsenullundefined は有効(valid)な値ではあるけど React が無視するのでレンダリングしません! ということです。だから上記のサンプルコートは結果的に全部空のdivをレンダリングすることになります。

こういう特徴を利用して&&演算子を用いた React コンポーネント制御ができます。Booleanだけ入るshowHeaderという変数があるとしましょう。以下のコードはshowHeadertrueのときだけHeaderコンポーネントをレンダリングします。showHeaderfalseのときは React が無視するはずですからね。

<div>
  {showHeader && <Header />}
  <Content />
</div>

しかし、Reactの公式サイトはこういう注意点も付けています。

One caveat is that some “falsy” values, such as the 0 number, are still rendered by React.

いくつかの falsy な値は無視されずにレンダリングされるということです。例では数字の0をあげてますが、空の文字列("")も同じく無視されずにレンダリングされる対象です。

// errorMessage が "" の場合、errorMessage のほうがレンダリングされる
{errorMessage && <Error />}

// itemPrice が 0 の場合、itemPrice のほうがレンダリングされる
{itemPrice && <Item />}

ここまで理解したらわかってきました。もう一回私のコードを見てみましょう。

<View>
  {imageUrl && <Image source={{ uri: imageUrl }} />}
</View>

imageUrlstring型で、""が入るときもあります。ですが、つい先話した通り文字列はたとえ空の文字列だとしても React は無視しません。だから&&の左にあるimageUrlは立派なTextタグの外に存在する文字列になってしまい、それに相応するエラーが発生したという流れです。

3. 解決

解決は結構簡単で、色々な方法があります。

3-1. 三項演算子

まずは&&ではなくて三項演算子を使う方法です。これだったらImageタグを返すかnullを返すかの二択なので、ルール違反ではありません。

<View>
  {imageUrl ? <Image source={{ uri: imageUrl }} /> : null}
</View>

3-2. Boolean 化

次はimageUrlを Boolean 化する方法です。

<View>
  {!!imageUrl && <Image source={{ uri: imageUrl }} />}
</View>
<View>
  {Boolean(imageUrl) && <Image source={{ uri: imageUrl }} />}
</View>

!!で強制的に Boolean 化することもできますが、個人的にはそんなに可読性が良い方法ではないと思いますので、好きではありません。Boolean()のほうがわかりやすい気がします。

3-3. 採用案

私が採用した方法はこれです。

<View>
  {imageUrl !== '' && <Image source={{ uri: imageUrl }} />}
</View>

imageUrl !== ''truefalseを返すので、これも Boolean 化の一種ですね。三項演算子を使う方法より&&を使ったほうが簡潔に書けるし、これだったら「imageUrlが空文字列ではない場合だけImgaeタグを使う」という意図を明確に伝えられると思ってこの方法を採用しました。

終わり

ぐぐってみたら2018年に同じ Issueがあがってました(結構前の Issue だった)

Discussion

ログインするとコメントできます