Open33

react.devを読んでいく

KeitaUenishiKeitaUenishi

Components can render other components, but you must never nest their definitions:

コンポーネントは他のコンポーネントをレンダリングすることができるが、決してその定義をネストしてはならない:

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

The snippet above is very slow and causes bugs. Instead, define every component at the top level:

上記のスニペットは非常に遅く、バグの原因になる。代わりに、すべてのコンポーネントをトップレベルで定義する:

export default function Gallery() {
  // ...
}

// ✅ Declare components at the top level
function Profile() {
  // ...
}
When a child component needs some data from a parent, pass it by props instead of nesting definitions.

When a child component needs some data from a parent, pass it by props instead of nesting definitions.

子コンポーネントが親コンポーネントから何らかのデータを必要とする場合、定義をネストする代わりにpropsで渡す。

たまに見たことあって読みづらいなーと思っていたけど、ちゃんと公式で否定されていた

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

KeitaUenishiKeitaUenishi

default exportとnamed exportについて
どっちがいいというわけではなく、チームに合った方向で最適なものを選択してください、とのことが書かれている

To reduce the potential confusion between default and named exports, some teams choose to only stick to one style (default or named), or avoid mixing them in a single file. Do what works best for you!

デフォルトと名前付きエクスポートの間の潜在的な混乱を減らすために、一部のチームは 1 つのスタイル (デフォルトまたは名前付き) のみに固執するか、単一のファイル内でそれらを混合しないことを選択します。自分にとって最も効果的なことをしてください!

https://react.dev/learn/importing-and-exporting-components#exporting-and-importing-multiple-components-from-the-same-file

KeitaUenishiKeitaUenishi

https://react.dev/learn/writing-markup-with-jsx#why-do-multiple-jsx-tags-need-to-be-wrapped

Why do multiple JSX tags need to be wrapped?
複数の JSX タグをラップする必要があるのはなぜですか?

JSX looks like HTML, but under the hood it is transformed into plain JavaScript objects. You can’t return two objects from a function without wrapping them into an array. This explains why you also can’t return two JSX tags without wrapping them into another tag or a Fragment.

JSX は HTML のように見えますが、内部ではプレーンな JavaScript オブジェクトに変換されます。関数から 2 つのオブジェクトを返すには、それらを配列にラップする必要があります。これは、2 つの JSX タグを別のタグまたはフラグメントにラップしない限り返すことができない理由を説明しています。

なるほど。オブジェクトを配列に入れて返してるのか。

KeitaUenishiKeitaUenishi

class → classNameの理由

Since class is a reserved word, in React you write className instead, named after the corresponding DOM property:

class は予約語であるため、React では代わりに、対応する DOM プロパティにちなんで名付けられた className を記述します。

https://developer.mozilla.org/en-US/docs/Web/API/Element/className
「Reactではそういうもん」という感じで理解していたけど、DOMプロパティにちなんでそうなっていたのね

KeitaUenishiKeitaUenishi

https://react.dev/learn/conditional-rendering#conditionally-returning-nothing-with-null

In practice, returning null from a component isn’t common because it might surprise a developer trying to render it. More often, you would conditionally include or exclude the component in the parent component’s JSX

実際には、コンポーネントから null を返すことは一般的ではありません。コンポーネントをレンダリングしようとしている開発者を驚かせる可能性があるためです。多くの場合、親コンポーネントの JSX にコンポーネントを条件付きで含めたり除外したりします。

KeitaUenishiKeitaUenishi

https://react.dev/learn/conditional-rendering#are-these-two-examples-fully-equivalent

Are these two examples fully equivalent?
これら 2 つの例は完全に同等ですか?

If you’re coming from an object-oriented programming background, you might assume that the two examples above are subtly different because one of them may create two different “instances” of <li>. But JSX elements aren’t “instances” because they don’t hold any internal state and aren’t real DOM nodes. They’re lightweight descriptions, like blueprints. So these two examples, in fact, are completely equivalent. Preserving and Resetting State goes into detail about how this works.

オブジェクト指向プログラミングのバックグラウンドがある場合は、上の 2 つの例は、どちらかが <li> の 2 つの異なる「インスタンス」を作成する可能性があるため、微妙に異なると考えるかもしれません。ただし、JSX 要素は内部状態を保持せず、実際の DOM ノードではないため、「インスタンス」ではありません。これらは設計図のような軽量の説明です。したがって、これら 2 つの例は実際には完全に同等です。「状態の保持とリセット」では、これがどのように機能するかについて詳しく説明します。

JSX要素は内部状態を持たない(immutableである)
状態が変更されたら、また新たにJSX要素が作られるため
↑ という理解

KeitaUenishiKeitaUenishi

0の罠がちゃんと書かれてた

Don’t put numbers on the left side of &&.
&&の左側には数字を入れないでください。

To test the condition, JavaScript converts the left side to a boolean automatically. However, if the left side is 0, then the whole expression gets that value (0), and React will happily render 0 rather than nothing.
For example, a common mistake is to write code like messageCount && <p>New messages</p>. It’s easy to assume that it renders nothing when messageCount is 0, but it really renders the 0 itself!
To fix it, make the left side a boolean: messageCount > 0 && <p>New messages</p>.

条件をテストするために、JavaScript は左側をブール値に自動的に変換します。ただし、左側が 0 の場合、式全体がその値 (0) を取得し、React は何も表示しないのではなく、喜んで 0 を表示します。
たとえば、よくある間違いは、messageCount && <p>新しいメッセージ</p> のようなコードを記述することです。 messageCount が 0 の場合は何もレンダリングされないと思われがちですが、実際には 0 自体がレンダリングされます。
これを修正するには、左側をブール値にします: messageCount > 0 && <p>Newmessages</p>。

KeitaUenishiKeitaUenishi

https://react.dev/learn/rendering-lists#why-does-react-need-keys

配列のインデックスやランダム生成したkeyではなく、なるべくデータに基づいたidなどをkeyにするのがよいとのこと

You might be tempted to use an item’s index in the array as its key. In fact, that’s what React will use if you don’t specify a key at all. But the order in which you render items will change over time if an item is inserted, deleted, or if the array gets reordered. Index as a key often leads to subtle and confusing bugs.

配列内の項目のインデックスをキーとして使用したくなるかもしれません。実際、キーをまったく指定しない場合、React はこれを使用します。ただし、項目が挿入、削除された場合、または配列の順序が変更された場合、項目をレンダリングする順序は時間の経過とともに変化します。インデックスをキーとして使用すると、微妙で混乱を招くバグが発生することがよくあります。

KeitaUenishiKeitaUenishi

Reactは純粋関数(同じ入力、同じ出力。同じ入力が与えられた場合、同じ結果を返す関数)の概念に基づいて設計されている。
作成するコンポーネントはすべて純粋な関数であるということが前提。

https://react.dev/learn/keeping-components-pure#purity-components-as-formulas

In the above example, double is a pure function. If you pass it 3, it will return 6. Always.

React is designed around this concept. React assumes that every component you write is a pure function. This means that React components you write must always return the same JSX given the same inputs:

KeitaUenishiKeitaUenishi

StrictModeがなんで2回レンダリングするのか全然わかってなかったけど、純粋ではない関数を検出するためだったのか……

https://react.dev/learn/keeping-components-pure#detecting-impure-calculations-with-strict-mode

React offers a “Strict Mode” in which it calls each component’s function twice during development. By calling the component functions twice, Strict Mode helps find components that break these rules.

React は、開発中に各コンポーネントの関数を 2 回呼び出す「Strict モード」を提供します。 Strict モードでは、コンポーネント関数を 2 回呼び出すことで、これらのルールに違反するコンポーネントを見つけることができます。

KeitaUenishiKeitaUenishi

useEffectを使うのは最後の手段
とにかくReactでのコンポーネントは純粋関数で書こう! とのこと

https://react.dev/learn/keeping-components-pure#where-you-can-cause-side-effects

If you’ve exhausted all other options and can’t find the right event handler for your side effect,
you can still attach it to your returned JSX with a useEffect call in your component.
This tells React to execute it later, after rendering, when side effects are allowed. However, this approach should be your last resort.

他のオプションをすべて使い果たし、副作用に適したイベント ハンドラーが見つからない場合でも、コンポーネント内の useEffect 呼び出しを使用して、返された JSX にイベント ハンドラーをアタッチできます。
これは、副作用が許可されるレンダリング後の後で実行するように React に指示します。ただし、この方法は最後の手段としてください。

KeitaUenishiKeitaUenishi

ReactというよりJavaScriptだけど、この辺曖昧だったのでメモ
https://react.dev/learn/responding-to-events#preventing-default-behavior

Don’t confuse e.stopPropagation() and e.preventDefault(). They are both useful, but are unrelated:
・e.stopPropagation() stops the event handlers attached to the tags above from firing.
・e.preventDefault() prevents the default browser behavior for the few events that have it.

e.stopPropagation() と e.preventDefault() を混同しないでください。どちらも便利ですが、無関係です。
・e.stopPropagation() は、上記のタグにアタッチされたイベント ハンドラーの起動を停止します。
・e.preventDefault() は、それが存在するいくつかのイベントに対するブラウザーのデフォルトの動作を防止します。

KeitaUenishiKeitaUenishi

https://ja.react.dev/learn/render-and-commit

レンダーは常に純粋な計算であるべきです。
同じ入力には同じ出力。同じ入力が与えられた場合、コンポーネントは常に同じ JSX を返す必要がある。(トマトサラダを注文した人がオニオンサラダを受け取ってはいけない!)
自分の仕事に専念する。レンダー前に存在したオブジェクトや変数を変更しない。(ある注文が他の誰かの注文を変更してはいけない。)

reactは純粋な関数(計算)であるべきとのこと。
各セクションで繰り返すのでかなり重要な思想だと理解。

KeitaUenishiKeitaUenishi

コンポーネントのメモリとしての state は、関数が終了したら消えてしまう通常の変数とは異なります。state は実際には React 自体の中で「生存」しています。まるで棚に保管しているかのように、関数の外部で存在し続けます。React がコンポーネントを呼び出すとき、React はその特定のレンダーに対する state のスナップショットを提供します。あなたのコンポーネントは、props やイベントハンドラの新たな一式を揃えた JSX という形で UI のスナップショットを返し、それらはすべてその特定のレンダー時の state の値を使って計算されます!

https://ja.react.dev/learn/state-as-a-snapshot

state = 変数
ではない。
レンダーごとにstateを用いてDOMが構築される。レンダーされる時は「その時のstate」がスナップショットとして用いられる。
という理解。

KeitaUenishiKeitaUenishi

言い換えると、state として格納するすべての JavaScript オブジェクトは読み取り専用として扱う必要があります。

https://ja.react.dev/learn/updating-objects-in-state#treat-state-as-read-only

以下、理解メモ。
オブジェクトのstateは技術的には変更可能。でもレンダリングは走らないので直接変更されても何も変わらない。

set関数に入れて再レンダーをトリガーしよう。
下みたいな書き方はちゃんと動く
https://ja.react.dev/learn/updating-objects-in-state#treat-state-as-read-only

KeitaUenishiKeitaUenishi

refのベストプラクティス
https://ja.react.dev/learn/referencing-values-with-refs#best-practices-for-refs

・ref を避難ハッチ (escape hatch) として扱う。ref が有用なのは、外部システムやブラウザ API と連携する場合です。アプリケーションのロジックやデータフローの多くが ref に依存しているような場合は、アプローチを見直すことを検討してください。

・レンダー中に ref.current を読み書きしない。レンダー中に情報が必要な場合は、代わりに state を使用してください。React は ref.current が書き換わったタイミングを把握しないため、レンダー中にただそれを読みこむだけでも、コンポーネントの挙動が予測しづらくなってしまいます。(唯一の例外は if (!ref.current) ref.current = new Thing() のような、最初のレンダー中に一度だけ ref をセットするコードです。)

stateはレンダー時のスナップショットとして値を保持するが、refは現在値を書き換えるとすぐに変更される。ref.currentの変更をReactは検知せず、再レンダリングが発生するわけではないため画面上に表示されているテキストなどは変更されない。

KeitaUenishiKeitaUenishi

React に DOM を同期的に更新、あるいは「フラッシュ (flush)」するよう強制することができます。これを行うには、react-dom から flushSync をインポートし、flushSync の呼び出しで state 更新をラップします。

https://ja.react.dev/learn/manipulating-the-dom-with-refs#flushing-state-updates-synchronously-with-flush-sync

通常、state更新関数によって再レンダリングが走ってからDOMが更新されるが、flushSyncを用いることで、ラップされたコードが実行された直後に、DOMを同期的に更新するようになる。

KeitaUenishiKeitaUenishi

refはフォーカスやスクロールのような「非破壊的」なアクションであれば問題は発生しない。
ただ、DOMを手動で書き換えるなどを行うと、React側の動作と競合する恐れがある。

React が管理する DOM ノードの変更は避けてください。React が管理する要素を変更しようとしたり、子要素を追加あるいは削除しようとすると、見た目の一貫性が失われたり、上記のようなクラッシュが発生することがあります。

https://ja.react.dev/learn/manipulating-the-dom-with-refs#best-practices-for-dom-manipulation-with-refs

KeitaUenishiKeitaUenishi

useEffectで第2引数に"[]"を指定すると、コンポーネントのマウント時(初めて表示される時)のみに処理が実行される。
コンソールで確認すると、useEffet内にあるconsole.logが2回実行されている → なぜ?
開発環境にて、コンポーネントがアンマウントされた時の処理を確認するため

このようなバグは、手動での徹底的なテストがないと見逃しやすいものです。これらをすばやく見つけるために、開発環境では React は、初回マウント直後にすべてのコンポーネントを一度だけ再マウントします。
"✅ Connecting..." のログが 2 回表示されることで、実際の問題に気付くことができます。つまり、コンポーネントがアンマウントされたときに接続を閉じるコードがないということです。

useEffect内でのfetch

開発環境では、ネットワークタブに 2 つのフェッチが表示されます。これには何の問題もありません。上記のアプローチでは、最初のエフェクトがすぐにクリーンアップされるため、ignore 変数のコピーが true に設定されます。そのため、余分なリクエストがあっても、if (!ignore) チェックのおかげで state に影響を与えません。

https://ja.react.dev/learn/synchronizing-with-effects#step-3-add-cleanup-if-needed