Open11

Reactの公式ドキュメントを読む

OsaOsa

UIの記述

https://ja.react.dev/learn/describing-the-ui

React ではこれらを、ネストして再利用できるコンポーネントにまとめることができます。

再利用できるのがポイントか?

React コンポーネントとは、マークアップを添えることができる JavaScript 関数です。

マークアップを添える?→普通にhtmlか

各 React コンポーネントは、ブラウザにレンダーされるマークアップを含んだ JavaScript 関数です。

React コンポーネントは JavaScript 関数である!

JSX は HTML によく似ていますが、少し構文が厳密であり、動的な情報を表示することができます。

動的な情報を表示?

このような既存の HTML がある場合は、コンバータを使って修正することができます。

へー、コンバータがあるんや

React コンポーネントでは、props を使ってお互いに情報をやり取りします。親コンポーネントは、子コンポーネントに props を与えることで、情報を渡すことができます。HTML の属性 (attribute) と似ていますが、オブジェクト、配列、関数、そして JSX まで、どのような JavaScript の値でも渡すことができます!

attribute と似ている??

utils.jsで使われているサイト(https://imgur.com/)気になる。世界のミーム集?

変数にJSXの配列を保持しているの気持ち悪い

export default function List() {
  const listItems = people.map(person =>
    <li key={person.id}>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Scientists</h1>
      <ul>{listItems}</ul>
    </article>
  );
}

コンポーネントを純粋に保つ

いくつかの 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 />
    </>
  );
}

↑の結果が guest #2から始まるのはなぜ...?
TeaSet実行前に実行されているから?

OsaOsa

初めてのコンポーネント

https://ja.react.dev/learn/your-first-component

React では、あなたのマークアップと CSS と JavaScript を、独自の “コンポーネント” と呼ばれる、アプリのための再利用可能な UI 要素にまとめることができます。

再利用可能な 👀
やっぱり再利用前提なのかな

Chakra UI や Material UI のような、React オープンソースコミュニティーで共有されている何千ものコンポーネントを使い、プロジェクトを一気にスタートさせることも可能です。

公式が Chakra UI, Material UI を推しているのか?

伝統的なウェブページの作成方法とは、ウェブ開発者が先にコンテンツをマークアップしてから、ユーザとのインタラクションを加えるために JavaScript をちょっと添える、というものでした。これはウェブにとってインタラクションが「あると嬉しい」レベルのものだった時代にはうまく機能していました。しかし今や、インタラクションはほぼすべてのサイトとあらゆるアプリで必要とされるものです。React は同じテクノロジを使っていますが、インタラクティビティ・ファーストになっています。すなわち React コンポーネントとは、マークアップを添えることができる JavaScript 関数です。

大事。インタラクティビティ・ファーストか。

React コンポーネントは普通の JavaScript 関数ですが、名前は大文字から始める必要があります。さもないと動作しません!

それね

括弧がないと、return の後にあるコードはすべて無視されてしまいます!

公式で stackoverflow 載っけているのか 👀
ECMAはこれ

大文字・小文字の違いに気をつけてください。
<section> は小文字なので、React はこれが HTML タグを指しているのだと理解します。
<Profile /> は大文字の P で始まっているので、React は Profile という名前の独自コンポーネントを使いたいのだと理解します。

あ、Reactでタグの先頭が大文字かをちゃんと見てるんだな(実装は見てない)

コンポーネントがほかのコンポーネントをレンダーすることはできますが、コンポーネントの定義をネストさせてはいけません。

export default function Gallery() {
  // 🔴 Never define a component inside another component!
  function Profile() {
    // ...
  }
  // ...
}

上記のコードはとても遅く、バグの原因になります。代わりに、すべてのコンポーネントをトップレベルで定義するようにしてください。

原因なんだっけ...?

OsaOsa

コンポーネントのインポートとエクスポート

https://ja.react.dev/learn/importing-and-exporting-components

元の例がどのようにして 2 つのコンポーネントファイルに分割されたか確認しましょう。
Gallery.js は:

  • Profile を定義しているが、これは同一ファイル内でしか使われてないのでエクスポートしていない。
  • デフォルトエクスポートとして Gallery コンポーネントをエクスポートしている。

同一ファイル内でしか使われない場合はエクスポートしないのか 👀

補足

.js というファイル拡張子が省略された、以下のようなファイルを見ることがあるかもしれません。
import Gallery from './Gallery';
React では './Gallery.js' でも './Gallery' でも動作しますが、前者の方がネイティブ ES モジュールの動作により近い方法です。

ネイティブESモジュールとは...?

OsaOsa

JSX でマークアップを記述する

https://ja.react.dev/learn/writing-markup-with-jsx

コンポーネントを書く手段はほかにも存在しますが、ほとんどの React 開発者は JSX の簡潔さを好んでいるため、ほとんどのコードベースで JSX が使われています。

それ

class は予約語なので、React では className を使います(対応する DOM プロパティが由来となっています)。

あ、ちゃんと由来あったんだ!

落とし穴

歴史的理由により、aria-* と data-* 属性は HTML 属性と同じようにハイフン付きで書くことになっています。

歴史的理由は気になる

function formatDate(date) {
  return new Intl.DateTimeFormat(
    'en-US',
    { weekday: 'long' }
  ).format(date);
}

Intl.DateTimeFormat 気になる
https://zenn.dev/mazamachi/articles/js-intl-date-time-format-performance

moment.js などのライブラリを使わなくて済むし、タイムゾーンの指定なども簡単なので、最近(2023-01-15 現在)のベストプラクティス的なやり方になっていると思う。

ほう、そうなのか

OsaOsa

コンポーネントに props を渡す

https://ja.react.dev/learn/passing-props-to-a-component

ReactDOM は HTML 標準に準拠しています

ちゃんと公式でも述べてくれているんだ

props のおかげで、親と子のコンポーネントを独立して考えることができるようになります。例えば、Profile で person や size を変更するときに Avatar 内でどう使われるかを気にしないでよくなります。

大事

通常は props オブジェクト全体を必要とすることはないため、個々の props へと分割代入します。

公式は分割代入派か

<Avatar {...props} /> のような JSX スプレッド構文ですべての props を転送できるが、多用は禁物!

それ

OsaOsa

条件付きレンダー

https://ja.react.dev/learn/conditional-rendering

実際には、レンダーしようとしている開発者を混乱させる可能性があるため、コンポーネントから null を返すことは一般的ではありません。代わりに、親コンポーネント側の JSX で条件付きでコンポーネントを含めたり除外したりすることが多いでしょう。以下はその方法です。

あ、null返さないんだ

これはシンプルな条件分岐の場合にはうまく動きますが、使いすぎないようにしましょう。条件のためのマークアップが増えすぎてコンポーネントが見づらくなった場合は、見やすくするために子コンポーネントを抽出することを検討してください。

大事

JavaScript の && 式 は、左側(条件)が true である場合、右側(今回の場合はチェックマーク)の値を返します。しかし、条件が false である場合、式全体が false になります。React は、false を JSX ツリーの「穴」と見なし、null や undefined と同様に、何もレンダーしません。

あ、確かにそうか、falseだから何もレンダーされないのか

上記のようなショートカットを使って簡潔にコードを記述するのが難しいと感じた場合は、if 文と変数を使用してみてください。let で定義した変数は再代入も可能ですので、最初に表示したいデフォルトの値 (name) を指定します。

letを使うのは違くない...?

  • これらのショートカットは一般的だが、プレーンな if が好きなら必ずしも使わなくて良い。

ほう

OsaOsa

リストのレンダー

https://ja.react.dev/learn/rendering-lists

key は動的に生成するのではなく、元データに含めるべきです。

おお

key をどこから得るのか
データソースの種類によって key を得る方法は異なります。

  • データベースからのデータ: データがデータベースから来る場合、データベースのキーや ID は必然的に一意ですので、それを利用できます。
  • ローカルで生成されたデータ: データがローカルで生成されて保持される場合(例:ノートを取るアプリにおけるノート)は、アイテムを作成する際に、インクリメンタルなカウンタや crypto.randomUUID()、または uuid などのパッケージを使用します。

ローカルで生成される場合はまずは生成するデータからユニークなものを探すのが良さそう?
そこでユニークなものがなかった場合に、ローカルでID生成かな

おお! crypto.randomUUID 出てる!
ulid はどうなのか?

キーは兄弟間で一意でなければなりません。ただし、異なる配列に対応する JSX ノードには同じキーを使用することができます。

これは同じコンポーネント内でも...?ちょっと気になった

OsaOsa

コンポーネントを純粋に保つ

https://ja.react.dev/learn/keeping-components-pure

JavaScript 関数の中には、純関数 (pure function) と呼ばれるものがあります。純関数とは計算だけを行い、他には何もしない関数のことです。

純粋性:コンポーネントとは数式のようなもの

コンピュータサイエンス(特に関数型プログラミングの世界)では、純関数 (pure function) とは、以下のような特徴を持つ関数のことを指します。

  • 自分の仕事に集中する。呼び出される前に存在していたオブジェクトや変数を変更しない。
  • 同じ入力には同じ出力。同じ入力を与えると、純関数は常に同じ結果を返す。

React は、あなたが書くすべてのコンポーネントが純関数であると仮定しています。

React のレンダープロセスは常に純粋である必要があります。コンポーネントは JSX を返すだけであり、レンダー前に存在していたオブジェクトや変数を書き換えしないようにしなければなりません。さもなくばコンポーネントは不純 (impure) になってしまいます!

一般に、特定の順序でコンポーネントがレンダーされることを期待してはいけません。y = 2x と y = 5x のどちらを先に呼ぶかなど問題にしてはいけないのです。これらの数式は互いに無関係に計算されるべきです。同じように、各コンポーネントは「自分のことだけを考える」べきであり、レンダーの最中に他のコンポーネントに依存したり他のコンポーネントと協調したりすることはありません。レンダーとは学校の試験のようなものです。各コンポーネントはそれぞれ、自分の力だけで JSX を計算する必要があるのです!

React には “Strict Mode” という機能があり、開発中には各コンポーネント関数を 2 回呼び出します。関数呼び出しを 2 回行うことで、Strict Mode はこれらのルールに反するコンポーネントを見つけるのに役立ちます。

あ、そういうこと!

元の例では “Guest #1”、“Guest #2”、“Guest #3” と表示される代わりに “Guest #2”、“Guest #4”、“Guest #6” と表示されてしまっていましたね。元の関数が純粋でなかったため、2 回呼び出すと壊れていたわけです。

なるほど。謎は解決。

これは “ローカルミューテーション (local mutation)” と呼ばれます。あなたのコンポーネント内のちょっとした秘密のようなものです。

へー

イベントハンドラは純粋である必要はありません。

ほう

いろいろ探してもあなたの副作用を書くのに適切なイベントハンドラがどうしても見つからない場合は、コンポーネントから返された JSX に useEffect 呼び出しを付加することで副作用を付随させることも可能です。これにより React に、その関数をレンダーの後(その時点なら副作用が許されます)で呼ぶように指示できます。ただしこれは最終手段であるべきです。
可能な限り、ロジックをレンダーのみで表現してみてください。これだけでどれだけのことができるのか、驚くことでしょう!

大事

React はなぜ純粋性を重視するのか?

純関数を書くことには、多少の習慣化と訓練が必要です。しかし、それは素晴らしいチャンスをもたらすものでもあります。
コンポーネントが異なる環境、例えばサーバ上でも実行できるようになります! 入力値が同じなら同じ結果を返すので、ひとつのコンポーネントが多数のユーザリクエストを処理できます。
入力値が変化しない場合、レンダーをスキップすることでパフォーマンスを向上できます。これが問題ないのは、純関数は常に同じ出力を返すため安全にキャッシュできるからです。
深いコンポーネントツリーのレンダーの途中でデータが変化した場合、React は既に古くなったレンダー処理を最後まで終わらせるような無駄を省き、新しいレンダーを開始できます。純粋性のおかげで、いつ計算を中断しても問題ありません。
我々が開発する React の新たな機能は常に、関数の純粋性を活用しています。データ取得からアニメーション、パフォーマンスの向上に到るまで、React パラダイムの威力はコンポーネントを純関数に保つことによって発揮されるのです。

大事すぎるな

OsaOsa

UI をツリーとして理解する

https://ja.react.dev/learn/understanding-your-ui-as-a-tree

レンダーツリーとは React のコンポーネントだけで構成されるもの
React のレンダーツリーを考えることにより、アプリがどのプラットフォームにレンダーされるのかとは独立して、React アプリを理解できるようになります。

確かに!!

React アプリにおいて、ツリー構造で関係性をモデル化できるものがもうひとつあります。アプリのモジュールの依存関係です。

依存関係ツリーは、React アプリを実行するためにどのモジュールが必要かを判断するのに役立ちます。通常、React アプリを本番環境用にビルドする際には、クライアントに送信するために必要な JavaScript をすべてバンドルにまとめるというビルドステップが存在します。これを担当するツールはバンドラと呼ばれ、バンドラは依存関係ツリーを使用することで、どのモジュールを含めるべきかを決定します。

OsaOsa

イベントへの応答

https://ja.react.dev/learn/responding-to-events

イベントハンドラ関数は:

  • 通常、コンポーネントの内部で定義されます。
  • イベント名の先頭に handle が付いた名前にします。
    また、JSX の中でイベントハンドラをインラインで定義することもできます。
<button onClick={function handleClick() {
  alert('You clicked me!');
}}>

または、より簡潔にアロー関数を使って記述することもできます。

<button onClick={() => {
  alert('You clicked me!');
}}>

これらのスタイルはすべて同等です。インラインのイベントハンドラは、短い関数の場合に便利です。

公式ではどれが良いとかそこまで言及されてなさそう