Open10

React devを再読する

SaeSae

Describing the UI

ポイント

  • ReactはUIをレンダリングするためのJSライブラリ
  • 再利用可能なコンポーネントという単位でまとめることができる
    • コンポーネントはマークアップ、CSS、JavaScriptを組み合わせたもの
    • マークアップはJSXで表現する
      • JSXはJavaScriptのシンタックスシュガー
  • ReactコンポーネントはJavaScriptの関数である
  • コンポーネントを純粋に保つこと
    • 呼び出される前に使用していたオブジェクトや変数の値を変えない
  • 同じ入力であれば毎回同じ出力になる

(よくない例)

let guest = 0;

function Cup() {
  // Bad: changing a preexisting variable!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}
  • Strict Mode
    • 開発中に各コンポーネントを2回呼び出し、純粋関数に違反しているものを確認する
  • データの更新やアニメーションの開始などの副作用を用いるものはイベントハンドラー内に配置する。配置できない物に関してはuseEffectを使用し、返却されたJSXにアタッチさせる
    • 副作用とは
      • レンダリング以外で何かしらの変化を起こすもの
  • 依存関係ツリー
    • アプリケーションがどのモジュールを使っていてい、さらにそれぞれのモジュールがどのモジュールに依存しているかを示す構造。
    • ビルドツールが関連するすべてのJSコードをまとめてバンドルする際に使用する
      • ルートコンポーネント
        • その下のコンポーネントに影響を与える
      • リーフコンポーネント
        • 頻繁にレンダリングされる
SaeSae

Importing and Exporting Components

ポイント

  • コンポーネントを再利用可能にするためにファイルを分割する
  • デフォルトインポート、名前付きインポートのどちらをコーディングスタイルを選択してもいいが、意味のある名前をつけることが重要
SaeSae

Writing Markup with JSX

ポイント

  • 単一のルート要素を返す
    • JSXはJavaScriptに変換される
    • 変換後のJavaScriptはReact要素を表すオブジェクトに変換される(babel)
    • JSでは値を返す際にオブジェクトや配列などにまとめる必要があるため、複数のJSXを返す際はそれらを1つのラップ要素にまとめて返す必要がある。
SaeSae

Passing Props to a Component

ポイント

  • propsはコンポーネントに対する唯一の引数
    • 親と子のコンポーネントを独立して考えることができる
      • 子は親から渡ってくるpropsがどのように計算されているかなどを知らなくていい
  • デフォルト値の設定について
    size={null}size={0}が指定された場合はデフォルト値は適用されない
function Avatar({ person, size = 100 }) {
  // ...
}
  • spred構文
    • spred構文は闇雲に使わない
      • 多くのコンポーネントで使用しているのであれば何か間違っている
        • childrenに置き換えられないかを考えてみる
function Profile(props) {
  return (
    <div className="card">
      <Avatar {...props} />
    </div>
  );
}
  • propsはイミュータブル(変更不可)
    • 一方方向のデータフロー
      • 予測可能性を高める
      • コンポーネントの再利用
      • 効率的な再レンダリング
    • Reactは新しいpropsを親から渡った時点で古いpropsは捨てメモリを解放する(ガベレージコレクション)
SaeSae

Conditional Rendering

ポイント

  • コンポーネントからnullを返すのは良くない。親コンポーネントで表示を制御すると良き。

よくない例

function Item({ name, isPacked }) {
  if (isPacked) {
    return null;
  }
  return <li className="item">{name}</li>;
}

よい例

function Item({ name }) {
  return <li className="item">{name}</li>;
}
function List () {
  return isPacked && <Item name={name}/> 
}
  • 論理積演算子(&&)
    • 条件がtrueの場合にレンダリングする。そうでない場合は何もレンダリングしない
    • 条件がfalseの場合、評価した値を返却する
const hoge = 0 && null;
console.log(hoge) // 0
const messageCount = 0;
return messageCount && <p>New messages</p>;

上記の例でも、0はfalsyな値なので式はfalseと評価され左辺の0が表示される

SaeSae

Rendering Lists

ポイント

  • filterやmapを使用してコンポーネントの配列に変換する
  • なぜkeyが必要なのか
    • 各コンポーネントが配列のどのアイテムに対応しているかを伝えることで正確に更新を行い、DOMツリーの更新を効率的に行うことができる。
  • <Fragment>にもkeyはつけることができる
  • keyの取得に関して
    • データがDBから取得したものであればDB内のid
    • ローカルでデータを生成した場合は、インクリメントカウンターやuuidのようなパッケージを使う
  • keyは兄弟同士で一意な値である必要がある
  • keyは変更しはいけない。keyを割り当てたらそのアイテムのライフサイクルを通じてkeyが変わらないようにする
  • indexをkeyに使する
    • アイテムが挿入・削除・並び替えされると配列の順序が変わりキーが意図した通りに機能しなくなる。結果としてバグにつながる可能性も。
SaeSae

Keeping Components Pure

ポイント

  • コンポーネントを純粋な関数として記述することでコードベースが大きくなってもバグから守ることができる。
  • コンポーネントを数式のように考える
    • 他のものに影響を与えてない
    • 同じ入力には常に同じ出力を返す
  • 副作用を引き起こす場所
    • 画面の更新、アニメーションの開始、データの更新は副作用と呼ばれる
    • レンダリング中ではなく、別タイミングで起こるもの
      • 基本的にはイベントハンドラー内で処理する
      • useEffectを使用する(最終手段)。必要でない限り避ける。
  • なぜ純粋性が大切なのか
    • 異なる環境で動かせる
    • パフォーマンス向上
    • propsやstateが変われば、再レンダリングをスキップできる。純粋な関数は予測可能でキャッシュも可能。
    • 途中でレンダリングをやり直せる
      • 深いコンポーネントをレンダリング中にデータの更新をされた場合でもReactは古いレンダリングを中断して新しいレンダリングを再開できる
    • Reactの新機能に対応しやすい
      • 新機能は純粋コンポーネントを前提として提供される
SaeSae

Understanding Your UI as a Tree

ポイント

  • UIをツリーとしてモデル化する=コンポーネントツリー
    • トップレベルコンポーネント
      • ルートコンポーネントに最も近いコンポーネント
      • それ以下のコンポーネントのレンダリングパフォーマンスに影響を与える
      • 複雑な処理を含むことが多い
    • リーフコンポーネント
      • ツリーの最下部に位置する
      • 子コンポーネントを持たない
      • 頻繁に再レンダリングする
  • モジュール依存ツリー
    • コンポーネントやロジックを個別ファイルに分割するとそれぞれがJSのモジュールとなる
    • 各ノードは1つのモジュールを表す
    • 各枝はモジュール内のimportステートメントを表す
    • あるモジュールが別のモジュールをimportするとその間に依存関係が生まれツリー構成が形成される。
  • モジュールの数が多いとバンドルサイズも増加(最終的にクライアントがダウンロードするJSファイル)
  • バンドルサイズが大きいとページのロードが遅くなる
  • 依存ツリーを分析することで不要なモジュールを削除しコード分割を行い最適化を行う

コンポーネントツリーとモジュール依存ツリーの違い

コンポーネントツリー

  • コンポーネントのみ対象
  • コンポーネントの親子関係を示す

モジュールコンポーネント

  • モジュール(JSファイル)単位
  • importの関係を表す
  • コンポーネント以外のロジックファイルなども含まれる
  • 直接importしていないファイルは含まれない