はじめに
なんとなく React を使っている状態を脱却したい。
「公式ドキュメントを読むべし」との先人たちの教えに従い、知っていることもあれば、新たな気づきも得られると思うのでやってみる。
今回は React 公式ドキュメント の REACTを学ぶ から、UI の記述 について学習する。
公式ドキュメントのコードは JavaScript なので、 TypeScript で書きながら学習を進めていく。
UI の記述
初めてのコンポーネント
<section>
は小文字なので、React はこれが HTML タグを指しているのだと理解します。<Profile />
は大文字のP
で始まっているので、React はProfile
という名前の独自コンポーネントを使いたいのだと理解します。
なんとなく気づいてはいたけど、そうだったんだ・・・。
コンポーネントのインポートとエクスポート
エクスポートの方法によって {}
が必要だったり、不要だったり。
これ、よく間違えるんだよね。
JSX でマークアップを記述する
JSX タグが複数あるときにラップしないといけない理由
JSX は HTML のように見えますが、裏ではプレーンな JavaScript オブジェクトに変換されます。関数から 2 つのオブジェクトを返したい場合、配列でラップしないといけませんよね。2 つの JSX タグを返したい場合に別のタグかフラグメントでラップしないといけないのも、同じ理由です。
へ〜〜〜。そうなんだ〜。裏でプレーンな JavaScript オブジェクトに変換してたんだ。
HTML を JSX に変換する コンバータ があるんだって〜。大量の HTML を JSX に変換したときは、使うのいいかもね!
JSX に波括弧で JavaScript を含める
チャレンジ問題の3問目はこんな書き方でもよさそう。
src={ `${baseUrl}${person.imageId}${person.imageSize}.jpg` }
テンプレートリテラルで書いてみた。文字列はシングル or ダブルクォーテーションでそのまま書けるけど、テンプレートリテラルは外側に波括弧がないとダメみたい。
コンポーネントに props を渡す
props とは JSX タグに渡す情報のことです。例えば
className
、src
、alt
、width
やheight
は、<img>
に渡すことのできる props の例です。
へ〜。これも props って言うのね。知らなかった〜。
props に、値が渡されなかった場合にフォールバックとして使うデフォルト値を指定したい場合、分割代入の中でパラメータ名の直後に
=
とデフォルト値を書くことができます。
props ってデフォルト値を指定することができるんだ〜。props が渡されなかったとき、undefined のときに使われる。
JSX スプレッド構文で props を転送する
そんなこともできるんだ!知らなかった!
けどこんなことも書いてある。
スプレッド構文は慎重に使ってください。この構文をあらゆるコンポーネントで使っているなら、何かが間違っています。多くの場合は、コンポーネントを分割して JSX として children を渡すべきというサインです。
分かっててやるならいいけど・・・って感じですね。
コードは短く書けるとかっこいいけど、短ければいいわけじゃない。汚いものに蓋をしちゃいけないって言われてる気がします。
children
プロパティを有するコンポーネントには、親に任意の JSX で「埋めて」もらうための「穴」が開いている、と考えることができます。children
は、パネルやグリッドのような視覚的に何かを囲む要素に使うことができます。
children ってあんまり使ったことがなかったから苦手意識が強かった。けど、この考え方はイメージしやすい!
言葉のまま理解するのが苦手だから、イメージが作れると理解が進む方のタイプかも。
条件付きレンダー
function Item({ name, isPacked }) {
return (
<li className="item">
{isPacked ? (
<del>
{name + ' ✔'}
</del>
) : (
name
)}
</li>
);
}
これはシンプルな条件分岐の場合にはうまく動きますが、使いすぎないようにしましょう。条件のためのマークアップが増えすぎてコンポーネントが見づらくなった場合は、見やすくするために子コンポーネントを抽出することを検討してください。React ではマークアップはプログラミングコードの一種ですので、変数や関数といった道具を利用して複雑な式を読みやすく整頓することができます。
return の中で三項演算子がたくさん出てくると、見た瞬間萎えるのは私だけ??
変数をうまく活用して、return の中はコンパクトにしたいな〜と思う。
変数名を直感的なものにしたいんだけど、英語力低すぎてセンスのない変数名にしがちなのが残念すぎる。
チャレンジ問題の2で、回答と同じ記述をすると、<i></i>
の中に書いた文字に :
が含まれた瞬間、Expression expected
という警告が出た。コード自体に問題はないようで、ローカル環境でうまく動作している。IntelliJ の設定の問題なのかな??理由は分からなかったけど、直接文字を書かずに変数に格納することで警告を回避することができた。
チャレンジ問題の3、三項演算子を if 文にリファクタリングしてくださいってあるけど、読みにくくはないからこのままでいいじゃんって思った(笑)
だけど、if 文からのオブジェクトへのリファクタリングは、コードの柔軟性が UP してなるほど〜!!!って感じでした。
リストのレンダー
短い
<>...</>
フラグメント構文ではkey
を渡せないため、これらを 1 つの<div>
にグループ化するか、やや長くてより明示的な<Fragment>
構文を使用する必要があります。
フラグメントは DOM から消え去るため、これにより<h1>
、<p>
、<h1>
、<p>
というように続くフラットなリストが生成されます。
そうそう。<>...</>
には key
を渡せない!
<div>
で括りたくないときは、<Fragment>
を使えばいいのか〜!!!省略しなければ key
を渡してもいいのね。
チャレンジ問題3、map の中で map を呼び出すのをやめて、コンポーネントに抽出するリファクタリング。
使ってる map の数は減ってないから、map をネストしていないように見せてるだけで、実質的には map をネストしてると思うんだけど・・・。
これは読みやすくなったのかな??
チャレンジ問題4、回答にあるよりスマートな書き方ができた気がする〜。
export default function Poem() {
return (
<article>
{poem.lines.map((line, index) => (
<Fragment key={index}>
<p>{line}</p>
{poem.lines.length - 1 > index && <hr />}
</Fragment>
))}
</article>
);
}
コンポーネントを純粋に保つ
純関数(pure function)
- 副作用を持たない
- 同じ入力を与えると、常に同じ結果を返す
React は、あなたが書くすべてのコンポーネントが純関数であると仮定しています。つまり、あなたが書く React コンポーネントは、与えられた入力が同じであれば、常に同じ JSX を返す必要があります。
そうだったんだ。意識したことはなかったけど・・・。
React には “Strict Mode” という機能があり、開発中には各コンポーネント関数を 2 回呼び出します。関数呼び出しを 2 回行うことで、Strict Mode はこれらのルールに反するコンポーネントを見つけるのに役立ちます。
console.log したときに、うっすら同じのが出力されてるのを見たことあるけど、わざと2回呼び出して純粋性を確認していたのね。そういうことだったのか〜。
<React.StrictMode>
でルートコンポーネントをラップすることで有効になる。本番環境には影響しない。React すごー!
React では、副作用は通常、イベントハンドラの中に属します。イベントハンドラは、ボタンがクリックされたといった何らかのアクションが実行されたときに React が実行する関数です。イベントハンドラは、コンポーネントの「内側」で定義されているものではありますが、レンダーの「最中」に実行されるわけではありません! つまり、イベントハンドラは純粋である必要はありません。
今更ながらイベントハンドラの定義を知った。
いろいろ探してもあなたの副作用を書くのに適切なイベントハンドラがどうしても見つからない場合は、コンポーネントから返された JSX に useEffect 呼び出しを付加することで副作用を付随させることも可能です。これにより React に、その関数をレンダーの後(その時点なら副作用が許されます)で呼ぶように指示できます。ただしこれは最終手段であるべきです。
可能な限り、ロジックをレンダーのみで表現してみてください。これだけでどれだけのことができるのか、驚くことでしょう!
useEffect って最終手段だったんだ・・・。カジュアルに使ってる印象あるけど、もっと考える余地があったりするのかな・・・。
チャレンジ問題、既にあるコードの修正なので、脳みそが疲れる・・・。
関数を純粋に保つ必要性を実感できました!
UI をツリーとして理解する
学習のまとめ、所感
各章の最後にあるチャレンジ問題が章の復習になってとてもよかったです!
チャレンジ問題をローカルにコピペして動かしてたんだけど、ローカルに作った環境は TypeScript だから型を入れなくちゃいけなくて。。。型が分からないのとかあって、chatGPT に聞きながらやってました。ReactNode って型とか知らなかったな〜。
おわりに
次は「REACT を学ぶ > インタラクティビティの追加」を学習します!