From b25b8b9873ffdfd0c94b5df51cb5a443c23931b7 Mon Sep 17 00:00:00 2001 From: "Daisuke.Hirano" Date: Mon, 23 Dec 2024 15:06:01 +0900 Subject: [PATCH] =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stateManagementForReact/index.md | 1 + react/docs/content/toc/02_element_render.md | 8 +++++ react/docs/content/toc/03_components_props.md | 2 ++ react/docs/content/toc/06_list_key.md | 3 ++ react/docs/content/toc/07_lifecycle.md | 8 +++++ react/docs/content/toc/08_state.md | 33 +++++++++++++++++++ react/docs/content/toc/11_object_is.md | 4 +++ react/docs/content/toc/12_effect.md | 24 +++++++++++++- react/docs/content/toc/15_advanced.md | 1 + 9 files changed, 83 insertions(+), 1 deletion(-) diff --git a/docs/miniLecture/stateManagementForReact/index.md b/docs/miniLecture/stateManagementForReact/index.md index ba36507a..acd5f088 100644 --- a/docs/miniLecture/stateManagementForReact/index.md +++ b/docs/miniLecture/stateManagementForReact/index.md @@ -11,6 +11,7 @@ React では、それらの状態管理を実現するにあたって複数の - useContext - Redux(Redux Toolkit) - Recoil +// Recoilはメンテナンスがされておらず、新規プロジェクトで採用されることはなさそうなので、zustandかjotaiを挙げておくのがよいのかなと思いました。 ## useState について diff --git a/react/docs/content/toc/02_element_render.md b/react/docs/content/toc/02_element_render.md index aae99de8..4d9e53b4 100644 --- a/react/docs/content/toc/02_element_render.md +++ b/react/docs/content/toc/02_element_render.md @@ -51,14 +51,22 @@ React が面倒を見ます。結果、DOM API を扱うよりもパフォーマ そして、以前の DOM と新しい DOM の差分を算出します。 +// 以前の仮想 DOMと新しい仮想DOMの差分を算出するのが正しいので、図が間違っていますかね? + ![仮想DOMのイメージ](./02_lesson2-2.png) 計算が終わると、リアル DOM は実際に変更されたものだけが更新されます。 +// "実際に変更されたもの"という文言が少し気になるので +「算出された差分のみ、リアルDOMが更新されます。」 の方がよいかなと思いました。 + ![仮想DOMのイメージ](./02_lesson2-3.png) ## 「秒刻みで動く時計」のサンプルで確認してみよう +// このサンプルでは、時刻部分だけではなく、すべての要素が更新されてしまうので、修正したほうがよいと思います。 +Reactの公式Docでは、propsとしてtimeを受け取るサンプルを使っています。[ステップ 3:React が DOM への変更をコミットする](https://ja.react.dev/learn/render-and-commit#step-3-react-commits-changes-to-the-dom) + 「秒刻みで動く時計」のサンプルで、再描画が必要な箇所のみ更新されていることを確認します。 ```javascript diff --git a/react/docs/content/toc/03_components_props.md b/react/docs/content/toc/03_components_props.md index f49ac93b..38a10542 100644 --- a/react/docs/content/toc/03_components_props.md +++ b/react/docs/content/toc/03_components_props.md @@ -48,6 +48,7 @@ class Welcome extends Component { 基本的には、下記の命名規則を使うことになります。 - コンポーネント名は、「**upper camel case**」(e.g. CamelCase) +// 命名規則ではなくて、守らなければならないReactのルールということを書いておきたいです。[関数を定義する](https://ja.react.dev/learn/your-first-component#step-2-define-the-function) - それ以外の変数名や関数名は、「**lower camel case**」(e.g. camelCase) - 定数名(固定値)は、「**snake case**」(e.g. SNAKE_CASE) @@ -286,6 +287,7 @@ $ TARGET=C03/Sample3 npm run dev コンポーネントの分け方は、Atomic Design の考え方が参考になるでしょう。 多くの開発案件で、Atomic Design が採用されています。 +// Atomic Designの他に、[bulletproof-react](https://github.com/alan2207/bulletproof-react)も上げておきたいです。 https://bradfrost.com/blog/post/atomic-web-design/ diff --git a/react/docs/content/toc/06_list_key.md b/react/docs/content/toc/06_list_key.md index dc2b23d0..a63f4864 100644 --- a/react/docs/content/toc/06_list_key.md +++ b/react/docs/content/toc/06_list_key.md @@ -70,6 +70,9 @@ $ TARGET=C06/Q1 npm run dev **【注意事項】** 配列の item の値を `key`属性の値とした場合、リストの値に重複があると描画の不具合を起こします。 +// どのような不具合が起こるのかを説明するか、公式Docへの参照URLを書いておいた方がよいと思います。[key によるリストアイテムの順序の保持 +](https://ja.react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key) + ```javascript [1, 2, 2, 3, 4, 2]; ``` diff --git a/react/docs/content/toc/07_lifecycle.md b/react/docs/content/toc/07_lifecycle.md index 6d7e1766..2494e524 100644 --- a/react/docs/content/toc/07_lifecycle.md +++ b/react/docs/content/toc/07_lifecycle.md @@ -3,6 +3,8 @@ title: "第7章 Reactのライフサイクル" --- ここでは、Reactコンポーネントにおけるライフサイクル(状態遷移)についての導入を行います。Reactの挙動を把握する上でライフサイクルは重要なポイントです。 +// 口頭でも良いので、ライフサイクルのなにが重要なのかやなぜ重要なのかを説明しておいた方がよいと思いました。 +// 個人的には、各Hooksの実行のタイミングとレンダリングとマウンティングの違いを知ることとが重要なのかなと思ってます。 ![lifecycle](./07_lifecycle.svg) @@ -14,8 +16,11 @@ title: "第7章 Reactのライフサイクル" |Unmounting(アンマウント) | コンポーネントが生成したDOMが削除されるとき | |Updating(再描画) | DOMの内容を更新するとき | |Render Phase | 純粋で副作用を伴わない処理。仮想DOM(VirtualDOM)と呼ばれる世界 | +// 「仮想DOM(VirtualDOM)と呼ばれる世界」だと意味が伝わりにくいので、いい感じに言い換えたいです。 |Commit Phase | DOM操作を含めた、副作用の実行。react-dom ライブラリの仕事 | +// 描画と書かれていますが、ブラウザが画面を描画する「ブラウザレンダリング」とは別なので、公式Docのように「コミット」という単語を使いたいです。 + # Hooks API Hooks API は、 **React v16.8** から導入された新機能で、 @@ -28,14 +33,17 @@ ES6クラスを書かずに実装できるのが、特徴です。 |:-- |:-- | | `useState` | コンポーネントの状態保存と再描画のスケジューリング | | `useEffect` | 副作用の実行 | +// 「副作用の実行」は誤解を与えるため、避けたいです。useEffect以外でも副作用は実行されます。React公式Docには、「useEffect は、コンポーネントを外部システムと同期させるための React フックです。」と書かれおり、「外部システムとの同期」としたいです。 | `useReducer` | `useState`の代替API。複数の状態管理に適する | | `useCallback` | 関数オブジェクトのメモ化を行う | | `useMemo` | 計算処理に時間がかかるケースで値のメモ化を行う | | `useContext` | 親コンポーネントの状態を `props` を経由しないで引き渡す API | | `useLayoutEffect` | ブラウザによって描画される前のタイミングで同期的に呼び出される関数 | | `useRef` | コンポーネント内のDOM要素の参照を取得する | +// 「コンポーネント内のDOM要素の参照を取得する」は誤解を与えるため避けたいです。React公式Docには、「useRef は、レンダー時には不要な値を参照するための React フックです。」と書かれており、「レンダー時には不要な値を参照する」としたいです。 | `useImperativeHandle` | **親** コンポーネント内のDOM要素の参照を取得する | | `useDebugValue` | React DevTools でカスタムフックのラベルを表示する | +// useSyncExternalStoreやuseTransitionなど、React18,19で追加されたHooksを追加するか考えたいです。個人的には、初学者をターゲットとしているので追加しなくても良いかなと思いました。追加しないのであれば、使用頻度が低いuseImperativeHandleとuseDebugValueは削除して良いのかなと思いました。 ![Hooks Lifecycle](./07_hooks_lifecycle.svg) diff --git a/react/docs/content/toc/08_state.md b/react/docs/content/toc/08_state.md index e766e4eb..02d8ef34 100644 --- a/react/docs/content/toc/08_state.md +++ b/react/docs/content/toc/08_state.md @@ -9,11 +9,36 @@ React では、「状態」のことを言葉通りに「state」と呼びます `props`は、コンポーネントの外から渡すもので、かつ、読み取り専用です。 一方、`state`は、コンポーネント自身が持つ情報で、かつ、書き換えができます。 +// レンダリング中は書き換えは出来ないですし、書き換えるにしてもmutableな変更は出来ないので、「書き換えができます」とは書きたくないです。 関数型言語では、**純粋関数** と **不純関数** という用語があります。 `state` を持つということは、そのコンポーネントは、不純関数です。 一方、`state`を持たず、副作用がないコンポーネントは、純粋関数です。 +// stateは副作用ですが、immutableなので、ある意味「引数のようなもの」と考えることもできます。 +公式Docの[純粋性が重要である理由](https://ja.react.dev/reference/rules/components-and-hooks-must-be-pure#why-does-purity-matter) には、「コンポーネントの入力とは props と state とコンテクスト。フックの入力とはその引数。」ともあります。 +stateを副作用の代表として取り上げたくないです。 + +「stateとは」という説明としては、以下の説明を + +[公式Doc 通常の変数ではうまくいかない例](https://ja.react.dev/learn/state-a-components-memory#when-a-regular-variable-isnt-enough) より、 +``` +コンポーネントを新しいデータで更新するためには、次の 2 つのことが必要です。 + +- レンダー間でデータを保持する。 +- 新しいデータでコンポーネントをレンダー(つまり再レンダー)するよう React に伝える。 +useState フックは、これら 2 つの機能を提供します。 + +- レンダー間でデータを保持する state 変数。 +- 変数を更新し、React がコンポーネントを再度レンダーするようにトリガする state セッタ関数。 +``` +を整理して、refと合わせて、 + +stateは、レンダリング間で値を保持したいもの、かつ、値が書き変わったときに際レンダリングしたい値を保持するもの +refは、レンダリング間で値を保持したいもの、かつ、値が書き変わったときに際レンダリングしたくない値を保持するもの + +と説明したいです。 + ![純粋関数と不純関数](./08_function.svg) | コンポーネント | 特徴 | @@ -39,6 +64,14 @@ const [state, setState] = useState(initialState); `useState`は、「現在の state(変数)」と「state を変更する関数」を返します。 このとき、実装者は、「現在の state」と「state を変更する関数」の名前を自由に決めることができます。`initialState`は初期状態です。型は何で指定できますが、Primitive Type(boolean, number, string)を指定してください。Object 型や Array 型も指定できますが、 その場合は、後章で説明する `useReducer` を使う方がよいでしょう。 +// 「Primitive Type(boolean, number, string)を指定してください」は言い過ぎだと思いました。公式Doc [関連するStateをグループ化する](https://ja.react.dev/learn/choosing-the-state-structure#group-related-state) では、関連するものはObjectにまとめることを推奨しています。 +stateをどのような型やまとまりで持つか、と、useReducerを使って描画とロジックを分離する、というのは別の話かなと思いました。 +公式Docにある通り、[深くネストされたstateを避ける](https://ja.react.dev/learn/choosing-the-state-structure#avoid-deeply-nested-state)くらいがちょうど良いと思いました。 + +// +このあたりで、stateが更新されることとpropsの更新が再レンダリングのトリガーであることを説明したいです。 + +// 初心者に向けてとなるとやや高度な内容ですが、大切だと思うので、[UI の状態を最小限かつ完全に表現する方法を見つける](https://ja.react.dev/learn/thinking-in-react#step-3-find-the-minimal-but-complete-representation-of-ui-state)について説明するか、リンクを貼っておくかしたいです。 ![useState](./08_useState.svg) diff --git a/react/docs/content/toc/11_object_is.md b/react/docs/content/toc/11_object_is.md index 455e28ef..47d2be33 100644 --- a/react/docs/content/toc/11_object_is.md +++ b/react/docs/content/toc/11_object_is.md @@ -181,6 +181,10 @@ React では、この問題を解決する Hooks API として `useCallback` を # useCallback の導入 +// useCallbackとReact.memoはセットで説明するのがよいと思いましたがどうでしょうか? +Reactが値の同一性を重視する理由をパフォーマンスに関連づけて説明したいです。 +「第11章 値の同一性を理解する」を「第14章 描画パフォーマンスの最適化」の前の章に移動した方がよいのかなと思いました。 + 関数オブジェクトの値の同一性を保証するには、**関数オブジェクトをメモ化**していく必要があります。 下記のようなメモ化されていない関数オブジェクトを例にメモ化の仕方について説明します。 diff --git a/react/docs/content/toc/12_effect.md b/react/docs/content/toc/12_effect.md index 80eb9bed..ee2f4d3e 100644 --- a/react/docs/content/toc/12_effect.md +++ b/react/docs/content/toc/12_effect.md @@ -32,6 +32,10 @@ title: '第12章 副作用を実行する' # useEffect API の導入 副作用をコンポーネントに含める場合は、`useEffect` Hooks API を使います。 +// 副作用を全てuseEffectに書くという誤解が生まれるので、「副作用をコンポーネントに含める場合、`useEffect` Hooks API あるいはイベントハンドラを使います。」と書くか、公式Doc通り、「useEffectは、コンポーネントを外部システムと同期させるためのReactフックです。」と書きたいです。 + +特に、イベントハンドラに書くべき処理をuseEffectに書いてしまう人が多い印象で、原因は、最初にReactを学ぶときに「副作用はuseEffect」と書いているためだと思います。 + 副作用となる処理をコールバックとして記述します。その副作用となる処理に依存する変数を 依存リストに含めます。 @@ -50,6 +54,8 @@ useEffect( 第 2 引数の依存リストを省略することで、副作用を描画の度に必ず実行します。 +// この例は出したくないです。ユーザーイベントがトリガーとなる副作用の実行は、イベントハンドラ内に書くべきであって、useEffectに書くのはアンチパターンとされています。 + 下記コードはその例です`Counter`コンポーネントが再描画される度に `useEffect`で実装した `document.title` が呼び出されます。 @@ -76,6 +82,7 @@ const Counter: FC = () => { ``` ## 副作用の実行を抑制したい +// このようにuseEffectの実行タイミングを依存配列によって制御するという考えはReactの思想には合わないので、例に出したくないです。 副作用を毎回実行したくない状況が発生することが想定されます。 そういうときは、`useEffect`の第 2 引数(依存リスト)を使います。 @@ -217,13 +224,16 @@ cf. Hooks API Reference – React ``` prettier を導入していた場合、依存リストに値の同一性が保証されている関数オブジェクトが自動的に挿入される場合があります。依存リストの自動挿入を無効化したい場合は、依存リストに対して、eslint-disable を適用してください。 - +// 細かいですが、prettierではなくてeslintだと思います。 ```javascript useEffect(() => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); ``` +// useEffectをつかったデータフェッチのサンプルコードがあってもよいと思いました。 +また、公式Docの[エフェクトを使ったデータフェッチ](https://ja.react.dev/reference/react/useEffect#fetching-data-with-effects)のように、"race condition"などの問題点について軽く触れて、useEffectをつかったデータフェッチの限界と、フレームワークやライブラリを使うことを推奨することを書いてもいいと思います。 + # useEffect のまとめ ## コンポーネントの描画の度に副作用を実行したい @@ -334,6 +344,18 @@ const CardPair: FC = () => { ## 【課題 12-1】Web API(Dummy)から取得したユーザプロファイルを表示する +// 2024年の新卒研修ではこの課題は例が良くないのでスキップになっていました。 +// 漆原さんは、「SuspenseはuseEffectを使わないという思想のコンポーネントなので、例がふさわしくない」とおっしゃっていたと思います。 +// React公式Docには、このように書かれています。 + +``` +サスペンスコンポーネントをアクティブ化できるのはサスペンス対応のデータソースだけです。これには以下が含まれます: +- Relay や Next.js のようなサスペンス対応のフレームワークでのデータフェッチ +- lazy を用いたコンポーネントコードの遅延ロード +- use を用いたキャッシュ済みプロミス (Promise) からの値の読み取り +サスペンスはエフェクトやイベントハンドラ内でデータフェッチが行われた場合にはそれを検出しません。 +``` + 「条件付きレンダー」と`useEffect`と`useState`の両方を使った課題です。 以下の実装の要件を満たしてください。 diff --git a/react/docs/content/toc/15_advanced.md b/react/docs/content/toc/15_advanced.md index 94b3166a..cfeb7632 100644 --- a/react/docs/content/toc/15_advanced.md +++ b/react/docs/content/toc/15_advanced.md @@ -1,6 +1,7 @@ --- title: 第15章 高度なアプリケーション実装に向けて --- +// 全体的に古い気がします。 * [React-Redux](https://react-redux.js.org/) * 多くの案件でReactと共に採用されているFluxフレームワーク