Photo by Markus Spiske
皆様どうも、こんにちは!
引き続きこまりのフロントエンドエンジニア、桑木です。
前回、ReactのオブジェクトをHTMLやDOMのプロパティ風に記述できるJSXの構文について説明しました。今回は、JavaScriptの式だけで記述する「繰り返し」について説明していきたいと思います。
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メソッドは、配列の要素1つずつを指定した関数の第1引数にして繰り返し実行し、関数の戻り値を新しい配列として返すメソッドです。配列のすべての要素を、要素ごとに指定した関数で変換して、新しい配列にマッピングする、ということですね。
とりあえず、例を見てみましょう。
var data = [
{name: "small", price: 150},
{name: "medium", price: 270},
{name: "large", price: 320},
];
<ul>
{
data.map( item => <li key={ item.name }>{ `${item.name} ${item.price}円` }</li> )
}
</ul>
このJSXからは次の結果が得られます。
<ul>
<li key="small">small 150円</li>
<li key="medium">medium 270円</li>
<li key="large">large 320円</li>
</ul>
見慣れないJavaScriptの構文があったでしょうか? あるとしたら、それはアロー関数とテンプレート文字列だと思います。
どちらもES6から導入された構文で、アロー関数は従来のfunction式とほぼ同じで、関数を値として得る構文です。基本的にはfunction式をより短く記述するために使用されます。
上記コードのul要素内の式は、
data.map(function (item) {
return <li key={ item.name }>{ `${item.name} ${item.price}円` }</li>;
})
と、記述した場合と同じ動きをします。
テンプレート文字列は恐らく見たまま想像通りの動作をしますが、文字列中への式の埋込み以外に改行などもそのまま記述できます。
さて、mapメソッドに指定した関数は、引数で受け取ったオブジェクトをReactのオブジェクトに変換する関数でした。mapメソッドによって配列の要素の数だけこの関数が実行されて、関数の戻り値のReactオブジェクトが格納された新しい配列が得られます。JSXに埋め込んだ式の結果が配列の場合、隣接した要素として配列の中身が表現されるので、最終的に上記コードに書いたような結果が得られます。
関数の中なら式以外も使える
前記のコードでは使用していませんが、結局のところ、関数の中ではif文でも何でも使えるのでmapメソッドに関わらず任意の関数を呼ぶだけで条件分岐を含む複雑な処理ができます。
では、JavaScriptの式だけで実現する「選択」とは何でしょうか? 次回はそれについて書きたいと思います。