if文はJSXの中で書け


はじめに

今日、インターン先オフィスでReactを勉強しているデザイナーさん2人と話していたところ、JSX内の条件文についてまとめ記事があったらいいよねという話になったので淡々とまとめていきたいと思います。簡潔にまとめたいので、前置きはこの辺にして早速始めます。

そもそもJSXってなに?

JSXとは、まるでHTMLを書いているかのようにReactを書くために作られたものです。なので

    return (
        <div>
            <h1>僕のホームページ</h1>
        </div>
    )

詳しいことは割愛しますが、このreturn()内のタグは実際にはHTMLのタグではないです。

return()内に来るのはHTMLのコードではなくJSXなので、たとえば


    const hoge = 'fuga'

    return (
        <div>
            <p>{hoge}</p>
        </div>
    )

このように{}で囲むことによって、JSX内に直接JavaScriptで定義した変数や定数(なんなら返り値のある関数でも)つっこむことができます。
その結果、上のコードから実際には


<div>
    <p>fuga</p>
</div>

というHTMLタグが生成されることになります。

本題:if文はJSXの中で書け

ReactでWeb開発をする際、次のような実装をする場面が出てくると思われます。

propsにてiconが定義されている場合のみ、ラベルとともにアイコンも表示するようなボタンを実装したい

普通のif文しか知らなかった頃の僕は、このような書き方をしていました。(お恥ずかしい、、笑)


    const HogeComponent = ({ icon, label }) => {  // propsはこのように{}で囲んだ中に直接定義することもできる

        if (icon === undefined) {  // iconが渡されていない場合
            return (
                <button>
                    {label}
                </button>
            )
        } else {
            return (
                <button>
                    {icon}
                    {label}
                </button>
            )
        }
    }

これ、iconを表示するかしないかだけの違いなのに同じことを2回書いてしまっています。

では、if文をJSXの中で書いてみましょう。

    const HogeComponent = ({ icon, label }) => {

        return (
            <button>
                {icon && <img src={icon} alt='icon' />}
                {label}
            </button>
        )
    }

随分とすっきり書けました。
なぜこのように書けるのか少し掘り下げてみましょう。

   {icon && <img src={icon} alt='icon' />}

ここに注目します。
これこそがJSX内のif文(正確にいうと条件式)で、iconは左辺、&&の後は右辺と呼ばれます。
この文は、左辺(つまり条件部分)がtrueであるときは右辺を、そうでないときは左辺を返します。
iconの中身は、

  • 定義されている時・・・画像データ
  • 定義されていない時・・・undefined

でした。
つまりアイコンが定義されているときは<img />タグ、定義されていない場合はundefinedが返されています。
ここからが重要です。
JSX内でundefinedfalsetruenullなどが返された場合、Reactはそれらを完全に無視します。
つまりは何もレンダー(描画)しません。

ここに関して、僕は長い間勘違いをしていました。上の条件式について、単純に条件おいて&&の後に描画したいコンポーネントおけばいいんでしょ?くらいにしか思っていませんでした。そのため、条件の部分にはtruefalseをとるものしか置いてきませんでした。

このような内部的な動きに関しても理解を深めておくことはとても重要なんだなと改めて認識した出来事でした。

左辺を返り値にしたくない場合

条件を満たさない場合でも左辺を返り値にしてしまいたくないとき、次のような書き方ができます。


    const HogeButton = ({ primary, label }) => {

        return (
            <button>
                {primary
                    ? <div className='primaryButtonInner'>{label}</div>
                    : <div className='normalButtonInner'>{label}</div>
                }
            </button>
        )
    }

ボタンがprimaryなのかどうかをtruefalseで渡されて、それに応じてボタンのインナーのスタイルを変えたいという場面です。
これは三項演算子と言います。primaryが真のとき?以降が、そうでないとき:以降が返されます。

(2020/10/2更新)

@thomasJs8 さんよりコメントをいただきました。

下の補足にもありますが、三項演算子は様々なところで使えるのでこのようにも書けます。


    const HogeButton = ({ primary, label }) => {

        return (
            <button>
                <div className={ primary ? 'primaryButtonInner' : 'normalButtonInner' }>{label}</div>
            </button>
        )
    }

条件分岐による影響範囲の大小により、どの範囲に三項演算子を適用するか都度考えて実装すると無駄のない綺麗なコードを書くことができます。

今回の内容には直接関係のない補足ですが、


const HogeFunction = ({ active }) => {

    const result = (active) ? 'hoge' : 'fuga'
    return result
}

三項演算子は上のようにJSX内以外でも使えます。(当たり前ですが)
この場合active === trueのとき文字列hogeが、active === falseのとき文字列fugaが返されます。
この書き方のいいところは、

const HogeFunction = ({ active }) => {

    if (active) {
        const result = 'hoge'
    } else {
        const result = 'fuga'
    }

    return result  // undefined
}

このようなスコープの違いにより実現できないコードを解決してくれることです。
それぞれのresultは{}で囲まれている、つまり、それぞれ閉鎖的なスコープに閉じ込められているため、スコープの外からは見えません。
そのため、HogeFunctionactiveの値と関係なくundefinedを返します。

(2020/10/2更新)

ここに関しても、@thomasJs8 さんよりコメントをいただきました。

ここで宣言している定数resultreturnするわけではない時は三項演算子を使うのが賢明ですが、returnしてしまいたい時

const HogeFunction = ({ active }) => {

    if (active) {
        return 'hoge'
    } else {
        return 'fuga'
    }
}

とif文内でreturnすることもできます。

最後に

今回紹介した書き方は、わりと既に広く使われているものだと思います。しかし、内部的にどのように動いているのか深く掘り下げて考えてみることに価値があると思い、まとめて投稿してみました。この記事が皆さんのReactに対する深い理解の一助になれば幸いです。

参考リンク

  • 条件付きレンダー

  • JSXを深く理解する