コーダーに知っておいてもらいたい React JSX の基礎 その3

スタッフブログ

Photo by Markus Spiske

皆様どうも、こんにちは!
引き続きこまりのフロントエンドエンジニア、桑木です。

前回、ReactのオブジェクトをHTMLやDOMのプロパティ風に記述できるJSXの構文について説明しました。今回は、かねてからの懸案である「繰り返し」について説明していきたいと思います。

JSX 中の JavaScript の値はどのように出力されるのか

「繰り返し」や「選択」を説明する前に、それと密接に関わってくる値のデータ型とその出力について説明します。

{ }で囲むことで、JSX中にJavaScriptの式を埋め込めることをその1で説明しました。式の結果の値が文字列や数値ならテキストノードとしてそのまま出力されます。JavaScriptには文字列と数値以外の基本型として論理値(false, true), null, undefinedがありますが、式の結果がこれらの場合は何も出力されない事がReactの仕様で決まっています。

そしてその他のオブジェクト型ですが、基本的に配列とReactのオブジェクトのみ正常に出力されます。配列は格納されている順番にそれぞれが隣接要素として出力されます。ReactのオブジェクトはReact.createElementなどで生成できますが、その1で説明したようにJSXを記述すればそれがReact.createElementに変換されるので、Reactのオブジェクトが必要な場面ではJSXを記述すれば問題ありません。

ところで、Reactではコンテンツの元データに変更があるたびにオブジェクトを生成しなおして、直前に生成したものと違いがないか比較します。比較の結果、違いのあった部分だけブラウザのDOMを変更することで無駄な再描画をなくして高速化を実現しています。

配列を出力する場合、その比較処理を更に高速化するためにそれぞれの要素にkeyという特殊な属性値を指定することが推奨されます。keyはReactが実際のDOMと紐付けるために使用します。

<ul>
{
    [<li key="A"><span>foo</span></li>, <li key={ 2 }>bar</li>, "tar"]
}
</ul>

同じ配列内でkeyが重複しないよう要素ごとに違う値を指定する必要があります。keyは文字列でも数値でも重複さえしなければ何でも構いません。そして、配列の直接の子要素以外(孫要素以下)にはkeyを指定する必要はありません。

上記のコードでは、説明のために固定の配列を記述していますが、実際にはプログラムで操作・生成した配列を式の結果として指定することになると思います。(もし固定の配列を書くことをひらめいたら、代わりにReact.Fragmentが使えないか検討してください)

JavaScript の式だけで繰り返しを実現する

さて、現実問題としてJSX中で繰り返しが使用したい場面というのは、何らかのデータを格納したオブジェクトの配列をリストや表などに整形して出力する場合だと思います。いくつか方法はありますが、その1つとして配列にES6で追加されたmapメソッドがよく使われます。mapメソッドは、配列のそれぞれの要素を指定した関数で実行して、その結果を新しい配列に格納して返すメソッドです。

とりあえず、例を見てみましょう。

// まずは適当にデータを用意します
var data = [
    {name: "small", price: 150},
    {name: "medium", price: 270},
    {name: "large", price: 320},
];

// 次のようなJSXを実行すれば、下の結果を得られます
<ul>
{
    data.map( item => <li key={ item.name }>{ `${item.name} ${item.price}円` }</li> )
}
</ul>
// ⇒
<ul>
    <li key="small">small 150円</li>
    <li key="medium">medium 270円</li>
    <li key="large">large 320円</li>
</ul>

見慣れないJavaScriptの構文があったでしょうか? あるとしたら、それはアロー関数テンプレート文字列だと思います。

どちらもES6から導入された構文で、アロー関数は従来のfunction式とほぼ同じで、関数を値として得る構文です。基本的にはfunction式をより短く記述するために使用されます。

上記コードの式は、

data.map(function (item) {
    return <li key={ item.name }>{ `${item.name} ${item.price}円` }</li>;
})

と、記述したのと同じ動きをします。

テンプレート文字列は恐らく見たまんま想像通りの動作をしますが、文字列中への式の埋込み以外に改行もそのまま記述できます。

さて、mapメソッドに指定した関数は、引数で受け取ったオブジェクトをReactのオブジェクトに変換する関数でした。mapメソッドによって配列の要素の数だけこの関数が実行されて、関数の戻り値のReactオブジェクトが格納された新しい配列が得られます。JSXに埋め込んだ式の結果が配列の場合、隣接した要素として配列の中身が出力されるので、最終的に上記コードに書いたような結果が得られます。

「JavaScriptの式だけで」とは言いましたが、関数の中ではif文でも何でも使えるのでmapメソッドだけでも色々な処理ができることがわかったと思います。しかし、単純な「選択」処理を関数化するのも手間が掛かります。次回は、関数を使わずにJavaScriptの式だけで「選択」を実現する方法を書きたいと思います。

コメント