Reactの続きです。前回は条件分岐のやってみました。今回はループ(繰り返し)です。条件分岐のif文と同様、JSXの中でfor文をそのまま書くとエラーになるので、その回避法とfor文以外の配列やmap()
を使った書き方をやってみます。それでは行きましょう!
【目次】
- JSXの中ではfor文も動かない
- JSXでfor文:失敗例(returnの使い方に注意!)
- JSXでfor文:成功例(push()を使う!)
- 配列の値をそのままJSXで読み込む
- map()メソッドを使う
- 最後に
前回記事
※参考:【React】条件分岐の書き方(if文エラー回避、論理演算子、三項演算子) - クモのようにコツコツと
Reactを習得するためにやったことまとめ
qiita.com
JSXの中ではfor文も動かない
前回の「条件分岐編」に引き続き掌田さんの「React.js & Next.js超入門」を参考に「ループ編」に入る。条件分岐はif文を使わない方法が紹介されていたが、やはりループでもfor文を使わない方法が紹介されている。
- 作者:津耶乃, 掌田
- 発売日: 2019/03/08
- メディア: 単行本
前回のif文でも触れたように、JSXの中ではfor文は使用できないためだ。
if statements and for loops are not expressions in JavaScript, so they can’t be used in JSX directly.
「ifステートメントとforループはJavaScriptの式ではないため、JSXで直接使用することはできません。」
しかし、if文と同様にfor文でもエラーを回避する方法があると思っているので、まずはそこからトライしてみる。
JSXでfor文:失敗例(returnの使い方に注意!)
JSX内に普通にfor文書いてみる(動かない)
まず、何も考えずにJSX内に普通にfor文を書いてみる。
See the Pen React 15 roop-5-ng1 by イイダリョウ (@i_ryo) on CodePen.
結果は…うーむ。やはり動いてないな。
HTMLコード
<section> <h1>はっぴいえんど</h1> <div class="text">読み込み中…</div> </section>
. text
のdivタグをReactで書き換えている。「読み込み中…」が表示されていると言うことはReactが動いていない。
JSコード
//DOM取得 const text = document.querySelector('.text'); //JSXに埋め込む値 const h2Text = "風街ろまん"; const pText = "(1971年発売)"; let song = [ '抱きしめたい', '空いろのくれよん', '風をあつめて', '暗闇坂むささび変化', 'はいからはくち', 'はいから・びゅーちふる', '夏なんです', '花いちもんめ', 'あしたてんきになあれ', '颱風', '春らんまん', '愛餓を' ]; //JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> <ul>{ for (let i = 0; i <song.length; i++) { return <li>{ song[i] }</li> } }</ul> </section> ); //レンダリング ReactDOM.render(elm, text);
- 変数
text
で.text
のDOMを取得 - 変数
h2Text
はh2
タグに表示したいテキスト - 変数
pText
はp
タグに表示したいテキスト - 変数
song
は配列でループで表示したいテキスト - 変数
elm
でJSXを設定
h2
タグにh2Text
を読み込み
p
タグにpText
を読み込み
ul
タグの中にfor文
for文の条件:変数i
を0から開始、song
の配列の回数まで繰り返し、i
を1ずつ加算
for文の処理:li
タグの中にsong
のi
番目のテキストを入れて返す ReactDOM.render
でelm
をレンダリングしてtext
に返す
はっぴいえんどのセカンドアルバム「風街ろまん」の情報を表示する、と言う内容。しかし、JSXの中にfor文を直書きするとif文と同様動かないことを確認できた。ここまでは予想通り。
なお、前回のif文ではJSXのタグをreturn
で返す必要があったため、今回もJSXのタグの前にreturn
を書いている。
※参考:【React】条件分岐の書き方(if文エラー回避、論理演算子、三項演算子) - クモのようにコツコツと
for文を即時関数に入れる(1回しか実行されない?)
if文では即時関数で囲うことで実行ができた。即時関数はこんな形。
//即時関数 (()=> { //処理 })();
※参考:JavaScriptで即時関数を使う理由 - Qiita
やってみる。
See the Pen React 15 roop-5-ng2 by イイダリョウ (@i_ryo) on CodePen.
お、動いた!が、1回しか実行されない。。
JSコード
//JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> <ul>{(() => { for (let i = 0; i <song.length; i++) { return <li>{ song[i] }</li> } })()}</ul> </section> );
ul
タグの中のfor文を即時関数で囲った。
むむむ、1回しか実行されないのはなぜか?
for文をJSX外部の関数として書く(結果は同じ)
前回、即時関数意外にJSX外部の関数として書いてもうまくいった。やってみる。
See the Pen React 15 roop-5-ng3 by イイダリョウ (@i_ryo) on CodePen.
ま、まあね。先程と結果は同じ。1回しか実行されない。
const roop = () => { for (let i = 0; i <song.length; i++) { return <li>{ song[i] }</li> } }; //JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> <ul>{ roop() }</ul> </section> );
- 変数
roop
の値は無名関数で処理はfor文 - 変数
elm
のJSXのul
タグの中でroop()
を実行
JSXは動かないがconsole.log()はループする
原因を調べる。まず、for文の中のJSXをコメントアウトして、代わりにconsole.log()
を実行してみる。
See the Pen React 15 roop-5-ng4 by イイダリョウ (@i_ryo) on CodePen.
JSコード
const roop = () => { for ( var i in song ) { console.log ( song[i] ); // return <li>{ song[i] }</li>; } };
JSXをコメントアウトしているため画面上では何も表示はされないが、デベロッパーツールのコンソールで見てみるとこんな実行結果になった。
"抱きしめたい" "空いろのくれよん" "風をあつめて" "暗闇坂むささび変化" "はいからはくち" "はいから・びゅーちふる" "夏なんです" "花いちもんめ" "あしたてんきになあれ" "颱風" "春らんまん" "愛餓を"
ちゃんとループでsong
の配列の値を読み込めている。
と言うことはsong[i]
と言う書き方自体には問題はない。
console.log()にもreturnをつける(一回しか実行されない!)
See the Pen React 15 roop-5-ng5 by イイダリョウ (@i_ryo) on CodePen.
JSコード
const roop = () => { for ( var i in song ) { return console.log ( song[ i ] ); // return <li>{ song[i] }</li>; } };
console.log()
にもreturn
をつけてみるとコンソールの結果はこうなった。
"抱きしめたい"
おお、一回しか実行されない!原因はreturn
か。
for文の中にreturnがあると処理が止まる
return
の役割について改めて調べると…
『return文』を使用すると関数(メソッド)が実行されたときにどの値を返すか指定することができます。そしてそれと同時にその関数の処理も終了させることができますので非常によく使われる構文になります。
そうか!値を返すだけでなく処理を終了させる、と言う役割もあるんだった!for文で何回分の処理を条件で設定しても、return
があると1回目の処理で終了してしまったわけだ。
これはこれで「true
の場合は以降の処理は無視」みたいな使い道には便利なのだが、今回の目的には相性が悪い。。
JSXのreturnを削除(予想どおり動かない)
for文の中にreturn
があると処理が止まる。では、return
を無くしてみようホトトギス。
See the Pen React 15 roop-5-ng6 by イイダリョウ (@i_ryo) on CodePen.
やはり、return
がないとJSXは動かない。
JSコード
const roop = () => { for ( var i in song ) { <li>{ song[i] }</li>; } };
さて、どうしたものか。
JSXでfor文:成功例(push()を使う!)
調べたら回避方法があった!
空の配列にpush()で値を追加する方法
こちらの記事にreturn
をfor文の外で使う方法があった。
即時関数の中でfor文を使う事例
{(() => { const items = []; for (let i = 0; i < 5; i++) { items.push(<li>{i}</li>) } return <ul>{items}</ul>; })()}
お、一手間かけているぞ。
変数items
に空の配列を作って、その中にpush()
メソッドで値を追加している。
そして値が入った配列items
をreturn
で返している。
これによってfor文の外でretuen
を使えるわけか!
※参考:React, JSX で条件分岐(If)やループ(For)を使う方法 │ Web備忘録
push() メソッドは、配列の末尾に 1 つ以上の要素を追加することができます。
※参考:Array.prototype.push() - JavaScript | MDN
さっそくやってんみる。
for文の中で直書きでやってみる(動かず)
まずはダメ元でfor文の中に直書きでやってみる。
See the Pen React 15 roop-3-ng by イイダリョウ (@i_ryo) on CodePen.
JSコード
//JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> { const items = []; for (let i = 0; i <song.length; i++) { items.push(<li>{ song[i] }</li>) } return <ul>{ items }</ul>; } </section> );
p
タグの下、変数items
に空の配列を入れる- for文で
song
の値の回数繰り返す
items
にpush()
でli
タグを追加。中身はsong
のi
番目のテキスト ul
の中にitems
を入れてreturn
で返す
まあこれが動かないことは想定内なので、確認作業。
for文を即時関数に入れる(成功!)
ここから本題。先程の処理全体を即時関数に入れてみる。
See the Pen React 15 roop-3 by イイダリョウ (@i_ryo) on CodePen.
やた!成功!風街ろまんの全曲目が表示された。
//JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> {(() => { const items = []; for (let i = 0; i <song.length; i++) { items.push(<li>{ song[i] }</li>) } return <ul>{ items }</ul>; })()} </section> );
p
タグの下のfor文の処理一式を即時関数で囲う
即時関数で囲う方法はやはり有効なんだな。
for文をJSX外部の関数として書く(成功!)
続いて、外部関数を読み込む書き方をやってみる。
See the Pen React 15 roop-4 by イイダリョウ (@i_ryo) on CodePen.
こちらも成功!
var roop = () => { const items = []; for (let i = 0; i <song.length; i++) { items.push(<li>{ song[i] }</li>) } return <ul>{ items }</ul>; }; //JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> { roop() } </section> );
- 変数
roop
の中でfor文の処理一式を書く p
タグの下でroop()
を読み込む
この方がJSXの中はシンプルになって見通しは良い。
配列の値をそのままJSXで読み込む
ここから先、掌田さんの「React.js & Next.js超入門」に書いてあったfor文を使わない繰り返しをやってみる。
テキストだけを読み込むと全部繋がってしまう
そもそもの話として、配列の値をJSX内でそのまま読み込む方法が紹介されていた。え?そんな簡単な話?
See the Pen React 15 roop-1-ng by イイダリョウ (@i_ryo) on CodePen.
こちらはあえて作った失敗例。HTMLのレンダリング結果はこちら。
HTMLコード
<ul> <li>抱きしめたい空いろのくれよん風をあつめて暗闇坂むささび変化はいからはくちはいから・びゅーちふる夏なんです花いちもんめあしたてんきになあれ颱風春らんまん愛餓を</li> </ul>
配列の値は読み込まれたがひと繋がりになっている。
JSコード
//JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> <ul><li>{ song }</li></ul> </section> );
ul
タグ内のli
タグでsong
の配列を直に読み込む。
li
タグの中でテキスト読み込みを繰り返すので当然こうなるな。
配列の値をliタグにする(成功!)
掌田さんの本ではこれに少し手を加えることで成功させている。
See the Pen React 15 roop-1 by イイダリョウ (@i_ryo) on CodePen.
おお!全曲がli
タグとして表示された!どうやっているのか?
let song = [ <li>抱きしめたい</li>, <li>空いろのくれよん</li>, <li>風をあつめて</li>, <li>暗闇坂むささび変化</li>, <li>はいからはくち</li>, <li>はいから・びゅーちふる</li>, <li>夏なんです</li>, <li>花いちもんめ</li>, <li>あしたてんきになあれ</li>, <li>颱風</li>, <li>春らんまん</li>, <li>愛餓を</li> ];
まず、変数song
の中の値をテキストではなくli
タグにしている。
//JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> <ul>{ song }</ul> </section> );
ul
タグの中で配列song
の値を読み込む。
これはめちゃめちゃシンプル!無理やりfor文を使わないでもこれで解決できるんだ!
ただ、配列song
の中の値にli
が入っているのは重複だなー。このタグに修正が生じると手間になりそう。
map()メソッドを使う
掌田さんの本にはもう一つmap()
メソッドを方法が紹介されていた。これによって上記の問題が回避できる!
map()メソッドとは
map()
メソッドとはなんぞや。配列を繰り返し処理してくれる関数のようだ。
「map」は配列データに使うメソッドであり、各要素1つずつに対して「コールバック関数」を実行し、その結果を新しい配列として返すことが出来るようになっています。つまり、この関数内に実行したい処理を書いておくことで、配列の各要素に対して好きな操作をすることが出来るというわけです!
var array = [ 配列データ ]; array.map( コールバック関数 );
※参考:【JavaScript入門】配列処理をするmap()の使い方とMapオブジェクトの解説! | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
map()メソッドでループを実行
さっそくやってみる
See the Pen React 15 roop-2-err by イイダリョウ (@i_ryo) on CodePen.
見ての通り、風街ろまんの全曲が表示されている!どんな書き方か?
JSコード
//JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> <ul> {song.map((val) => <li>{val}</li> )} </ul> </section> );
ul
タグの中、song
でmap()
メソッドを実行map()
メソッドの引数は無名関数- 無名関数の引数は
val
li
タグの中でval
を読み込む
for文とpush()
メソッドを使った方法よりもグッとスッキリしている!空の配列を作る必要もないしreturn
もいらない。
keyがないとコンソールエラーに。。
ただし、上記のコードだとコンソールにこんなエラーが表示された。
"Warning: Each child in a list should have a unique 'key' prop.%s%s See https://fb.me/react-warning-keys for more information.%s" " Check the top-level render call using <ul>." "" " in li"
翻訳すると
「警告:リスト内の各子には一意の「キー」プロパティが必要です。%s%s詳細については、https://fb.me/react-warning-keysを参照してください。%s "" <ul>を使用して最上位のレンダリング呼び出しを確認します。 "" "" 李で」
うーん、よくわからんがkey
と言うものが必要らしい。
React公式サイトにもこうあった。
このコードを実行すると、「リスト項目には key を与えるべきだ」という警告を受け取るでしょう。“key” とは特別な文字列の属性であり、要素のリストを作成する際に含めておく必要があるものです
※参考:リストと key – React
keyとは何か(indexは非推奨)
先程のReact公式サイトにkey
を含める方法が書いてあった。
Key は、どの要素が変更、追加もしくは削除されたのかを React が識別するのに役立ちます。配列内の項目に安定した識別性を与えるため、それぞれの項目に key を与えるべきです。
const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> );
※参考:リストと key – React
toString()
は数値を文字列に変換するメソッド。toString()
で配列のindex
(1、2、3などの連番)をわざわざ文字列に変換している。
※参考:【JavaScript入門】toStringで数値を文字列に変換(日付/基数変換) | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
React公式サイトによると、indexをそのまま使うのは推奨しないらしい。配列の値の順番や数が変わる可能性があるためとのこと、
レンダリングされる要素に安定した ID がない場合、最終手段として項目のインデックスを使うことができます
要素の並び順が変更される可能性がある場合、インデックスを key として使用することはお勧めしません。パフォーマンスに悪い影響を与え、コンポーネントの状態に問題を起こす可能性があります。
※参考:リストと key – React
実際に、indexを使った場合に生じ得る事態がこちらで解説されている。
※参考:Reactのリストコンポーネントのkeyにindexを使ってはいけない - 天の光はすべてバグ
keyを追加する(コンソールエラーなし!)
keyを追加してみる。
See the Pen React 15 roop-2 by イイダリョウ (@i_ryo) on CodePen.
結果は変わらない。
JSコード
//JSXの中身 const elm = ( <section className="h2_elem"> <h2>{ h2Text }</h2> <p>{ pText }</p> <ul> {song.map((val) => <li key={val.toString()}>{val}</li> )} </ul> </section> );
li
タグにkey
属性、val
のインデックスをtoString()
メソッドで文字列に変換
これによってコンソールエラーは無くなった!
なお、レンダリング結果のli
タグの中にkey
属性は描画されないようだ。内部的な処理なのかな。
最後に
と言うことでReactでループ処理をいくつか試してみました。
方法 | 備考 |
---|---|
即時関数にfor文 | push() メソッドで空の配列に追加。JSXの構造が膨らむので直感的じゃない。 |
外部の関数にfor文 | push() メソッドで空の配列に追加。それぞれの関数の中がシンプルなので見通しは良さそう。 |
配列の値を読み込む | シンプルな書き方だが、配列の値にli を書くのが重複になる。 |
map() でループを実行 |
シンプルな書き方!keyを書かないとコンソールエラー(indexは非推奨) |
for文はreturn
を使うと処理が止まってしまうため、空のpush()
メソッドに入れるのが少し手間に感じました。
配列の値を読み込むのは構造は最もシンプルですが値にli
を書くのが重複に感じました。
map()
メソッドで書く方法がシンプルで使いやすそうに感じました!ループはこちらをメインで使って行こうかと思います。
それではまた!
Reactを習得するためにやったことまとめ
qiita.com