udemy React完全入門ガイド学習メモ (original) (raw)
udemyのReact完全入門ガイドを視聴したので、新しく学んだことをメモしました。
4 章 React に触れる
package.json
のstart
は開発環境でサーバーを動かすコマンドpackage.json
のbuild
は本番環境用にビルドするコマンドpackage.json
のeject
はcreate-react-app
で作った隠しファイルを表示するコマンド- Vite でも create-react-app でも React 自体の挙動は変わらない
- Vite での
SWC
はより高速にビルドを行うためのツールで、容量は重い import
で相対パスを使う場合は同じ階層には./
をつける必要がある- 基本的にコンポーネントは 1 つのファイルにつき 1 つだけなので、デフォルトエクスポートを使えばいい
- Fragment には属性は指定しないが、
key
を使うことはある、その場合Fragment
は省略できない - JSX では boolean の値は表示されない
- 変数に代入できるものが式で、できないものが文
- 文は
return
の中以外なら問題なく動作する - props にもデフォルト引数を使える
- props の値を子コンポーネントで書き換えてはいけない
- JSX はあくまで JavaScript のオブジェクト
- JSX の実体は
React.createElement
メソッドで生成される - コンポーネントも React 要素
- React 要素を利用可能な状態にすることをマウントという
- ReactDOM が仮想 DOM を元に DOM を更新する
- 関数コンポーネントは props を受け取り JSX を返す
5 章 イベントリスナと状態管理
- イベントに渡す関数に
()
をつけると実行されて戻り値が渡されてしまう useState
はコンポーネントの再レンダリングを React に依頼し、変更した値を保持する仕組み- 更新関数によってコンポーネントの再レンダリングが行われる
memorizedState
とnext
を使うことでuseState
を呼び出した順番を管理している- 更新関数による再レンダリングは即時に行われるのではなく予約される
- オブジェクトの state は同じ構造で更新する必要がある
- オブジェクトの state は新しいオブジェクトで更新する必要がある
key
はコンポーネントを一意に識別する- React 要素のツリーにおけるコンポーネントの位置が変わらない場合は state は保持される
6 章 制御構文とフォームの制御
- React は差分検出で DOM を更新する
- key があるとどの要素が変更されるか識別できるので差分のみ更新できる
- key には配列のインデックスはなるべく使わない
textarea
の値は HTML とは違ってvalue
に書くselect
で選択された値を表示したい場合はoption
にselected
を指定するのではなく、select
のvalue
にoption
のvalue
と同じ値を指定する
7 章 スタイリング
- React では以下の 4 つの CSS の適用方法がある
- インラインスタイルは
style
属性を使わなければならないのでコードの再利用性が低く、また疑似セレクタとメディアクエリが使えず、パフォーマンス的にも良くない - 画面全体に適用する CSS は外部 CSS で読み込む
- CSS Modules は class を JavaScript のモジュールのようにインポートして使う方法
- CSS Modules は近い将来廃止される可能性があるので CSS in JS を使うべき
- CSS Modules を使うと自動で class が一意になる
- CSS in JS は
styled-components
やemotion
などのライブラリを使う styled-components
はスタイルを当てたコンポーネントを作って使う方法styled-components
はstyled()
を使ってコンポーネントのスタイルを継承できるstyled-components
も自動で class が一意になるstyled-components
では props を使って動的にスタイルを変更できる- ChakraUI は
styled-components
を元にした CSS フレームワーク - CSS フレームワークはコンポーネントをそのまま使うタイプと、class を当てていくタイプがある
8 章 DOM 操作
createPortal
はポータルの子要素を直接の親要素ではなく別の DOM 要素にマウントすることができる- バブリングはイベントが子要素から親要素に伝播すること
- React では直接 DOM を操作しないが、
useRef
を使うと DOM を直接操作できる useRef
の使い方useRef
で Ref オブジェクトを作る- 取得したい DOM の
ref
に Ref オブジェクトを指定する - React は DOM への参照を
current
に格納する - イベントハンドラなどで DOM にアクセスする
useRef
は DOM のメソッドにアクセスする場合に使われるuseRef
のcurrent
プロパティに値が設定される- ref は再レンダリングされても情報が保持される
- ref の値を変更しても再レンダリングされない
- ref を props として渡す際は、
ref
とは別の名前で受け取るか、forwardRef
を使う - ref を受け渡すのはコンポーネントの結びつきが強くなるのでやめたほうがいい
useImperativeHandle
を使うことで ref の操作を限定できるforwardRef
はuseImperativeHandle
は子から親にデータが流れるのであまり使わずに、state で解決したほうがいい- React のデフォルトではコンポーネントが他のコンポーネントの DOM にアクセスすることはできない
10 章 関数型プログラミング
- React は React Hooks 導入により関数型プログラミングにシフト
- 関数型プログラミングとは、手続き型の制御を関数に分離して隠蔽し、コードを整理するプログラミング手法
- 手続き型プログラミングは、手順通り命令を記述する手法
- 実際は関数型と手続き型は混在する
- 関数型では状態と処理を切り離すこと、特定の入力には特定の出力を返す純粋関数、一度設定した値を書き換えないことが重要
- 関数型では関数の引数にデータを渡して結果を返して処理を行う
- 純粋関数は決まった引数からは決まった戻り値を返す関数で、関数外のデータを参照・変更せず、関数外に影響を及ぼさない
- 引数で渡された値は変更してはいけない
- イミュータブルな値の変更は、値を書き換えるのではなく、新しい値をメモリ空間に作成し、参照を切り替える
- Immutability は配列やオブジェクトをイミュータブルな値のように元の値を変更せずに扱うこと
- 関数コンポーネントは props を受け取り、JSX を返す純粋関数にする(実際は state にも依存する)
- props は変更してはいけないので、更新関数も props として渡すようにする
- React ではコンポーネント外のものは基本的に引数だけを見ればいい
11 章 様々な状態管理の方法
useReducer
はuseState
の書き換えのようなもので、規模が大きいアプリに向いているuseState
は状態の更新の仕方を利用者が決め、useReducer
は更新の仕方もあらかじめ状態側が決めるuseReducer
の使い方- 現在の状態を保持する
state
とどのように状態を更新するかを示すオブジェクトであるaction
の 2 つの引数を取るreducer
関数を定義する useReducer
をインポートして、第一引数にreducer
関数、第二引数に初期値を渡して、分割代入でstate
と状態を更新するトリガーとなるdispatch
関数を定義する- 任意の関数で
dispatch
を使って、action
をreducer
に送り、状態の更新をトリガーする
- 現在の状態を保持する
action
はどの更新方法を実行するかを決め、switch 文で条件分岐を書くのが一般的useReducer
の第一引数は純粋関数になっている- input の
value
の値は文字列になるので、数値として扱う場合は変換する必要がある useContext
の使い方createContext
をインポートして、コンテキストを作る(初期値を指定する場合は引数に渡す)- コンテキストの
Provider
で囲んで値を渡す - 作成したコンテキストを使いたい場所でインポートして、
useContext
で値を定義する
Provider
やuseContext
は別ファイルで定義したほうが良い- Context を使っているコンポーネントは context が更新されると再レンダリングされる
- 不要な再レンダリングは値ごとに
Provider
を作ると防げる
12 章 useEffect とカスタムフック
- 依存配列が更新されると
useEffect
のコールバック関数が実行される - 依存配列を
useEffect
内で更新すると無限ループになる useEffect
のコールバック関数内で関数を返すと、コンポーネントが削除されたときに実行される(クリーンアップ処理)- クリーンアップ処理はコンポーネントが消滅するタイミングで後始末を行いたいときに書く
setInterval
は使用が終わったらメモリのためにクリアする必要があるuseLayoutEffect
はuseEffect
と同じように書けるが、useEffect
より先に実行される、また画面の反映よりも先にコールバック関数が実行されるuseEffect
は上から実行される- カスタムフックは hooks を内部で使った関数のこと
- JSX の構築に関係ない処理はすべて副作用なので、
useEffect
かイベントハンドラに記述する
13 章 Redux と Redux Toolkit
- Redux は
useContext
とuseReducer
を合わせたような状態管理の仕組みで、React 以外でも使える - Redux を使うことで、
useContext
とuseReducer
よりも保守性や可読性が上がるので中規模のアプリでも Redux を使ったほうがいい - Redux Toolkit を使うことが推奨されており、Redux 以外の便利なライブラリや関数も使うことができる
- Redux でも state を使いたいコンポーネントを
Provider
で囲む - state は
Store
に保持する - state を使う時は
useSelector()
を使う - state を更新する時は
useDispatch()
を使う - Redux の仕組み
- 1 つのグローバルストアが準備されていて Reducer を登録する
- Reducer によって Store に登録されている state を更新する
- Reducer を実行するために Dispatch で Action が Store に送られる
- Action の Type に一致する Reducer が実行される
- Redux では state を更新するのは Reducer だけになる
- Store は複数の Reducer を保持できる
- 複数の Reducer を使うと、Reducer ごとに state が作られるので、同じ type があるとどちらも変更されるので注意する(toolkit を使うと自動で別になる)
ActionCreator
は dispatch で渡すaction
を定義する関数で、toolkit では自動的に作られる- toolkit ではストアは
configureStore
で定義する - toolkit では
createSlice
に渡すオブジェクトで初期値やreducer
を定義する - toolkit では reducer は
createSlice
に渡すオブジェクトのreducer
プロパティとして定義し、reducer
プロパティに渡すオブジェクト内のメソッドで処理を記述する - toolkit の中では Immer が使われているので、ミュータブルな操作がイミュータブルに扱われる
- プロキシによってミュータブルな処理がイミュータブルな処理に変換される
- Immer を使うとネストされたミュータブルな値にも対応できる
- Immer を使ってミュータブルな値を変更した場合は
return
を書かない - Redux での副作用のコードは Middleware に書く
- Reducer に非同期処理は書かない
- すべての action に対して共通処理を書きたい場合は
middleware
を使う(あまり使うことはない)
14 章 クラスコンポーネント
- 関数コンポーネント以前はクラスコンポーネントを使っていた
- React の 16.8 から Hooks が追加されたので関数コンポーネントが使われるようになった
- クラスコンポーネントの問題点
- ロジックとビューの分離が難しい
- ステートの管理が複雑になるとロジックや副作用の整理が難しい
this
やbind
を使う必要がある
- クラスコンポーネントは
Component
をインポートして継承して作る - JSX は
render()
のreturn
に記述する - クラスコンポーネントは strict モードなので、メソッドをそのまま
this.method
で呼び出すと、メソッド内のthis
はundefined
になる - クラスコンポーネントの
this
の挙動を解決するにはbind
を使ってthis
をクラス自身に束縛する(コンストラクタ内で実行する) - コンポーネントには Mounting、Updating、Unmounting の 3 つのライフサイクルがある
- それぞれのライフサイクルで実行できるライフサイクルメソッドが存在する
- 関数コンポーネントではライフサイクルは
useEffect
で実現する - コンポーネントが初回にマウントされた時 componentDidMount =
useEffect(.., [])
- state が更新された時 componentDidUpdate =
useEffect(.., [val])
- コンポーネントが消滅する時 componentWillUnmount =
useEffect(() => {return () => {}}, [])
ErrorBounday
で囲むと全体のエラーを表示せずに、エラー時の UI を表示できるErrorBounday
はクラスコンポーネントでしか実装できない
15 章 パフォーマンスの最適化
- React で画面を更新する流れ
- 初回のレンダリングはルートコンポーネントを HTML にマウントした時
- state が更新された時に再レンダリングが走る
- 現在地と同じ値で更新を行うと React は子のレンダーや副作用の実行を回避して処理を終了する
- 再レンダリングはそのコンポーネントの子コンポーネントもすべて行われる
- state の変化は内部では
Object.is
で検知する - StrictMode だと React がレンダリングを実行してバグがないか確認するので、開発環境では余分にレンダリングが実行される(本番では実行されない)
StrictMode
はルートコンポーネントを囲むのが一般的React.memo
は囲んだコンポーネントが受け取る props が更新されない限り、再レンダリングが実行されなくなる- 子コンポーネントの props で関数を受け取る場合、親コンポーネントで渡したい関数を
useCallback
で囲むと、再レンダリングが発生しても再定義されず同じ関数が渡されることになる useCallback
では第二引数で依存配列を指定する必要があるuseCallback
内の関数で state を更新する場合は依存配列にその state を指定するuseMemo
はコールバック関数で return するあらゆる値をメモ化できる(キャッシュする)useMemo
自体の判定処理もコストがかかるstartTransition
は処理を遅延しているだけなので、メモ化を行なってもパフォーマンスが解決しない場合に検討すべき- React18 で導入された Concurrent Mode は処理の優先順位付けを行なってより快適でレスポンシブな画面を作成する機能
16 章 Rest API を使ったサーバーとの通信
- React でサーバーと通信を行う場合は、React のコードでサーバーにリクエストを送り、サーバーが JSON でレスポンスを送信するのが一般的
- REST API はサーバーへのリクエスト方式のこと
- REST API を使わない場合は
/create-todo
や/delete-todo
のように処理ごとに URL のパスを設ける - REST API の場合はリソースごとに URL を作り、HTTP メソッドで処理を分ける
- JSON は JavaScript のオブジェクトのようなデータ定義言語
.json
はコメントをかけないが、.jsonc
とするとコメントを書ける- JavaScript のオブジェクトを JSON に変換するには
JSON.stringify
を使う - JSON を JavaScript が認識できる形に変換するには
JSON.parse
を使う(サーバーからのレスポンスなど) - JSON server は開発環境用の API のモックを作成できる node.js のライブラリ
useEffect
のコールバック関数にasync
をつけるとエラーが出るので、非同期処理の関数を作るaxios
を使うと JSON を内部で JavaScript のオブジェクトに変換してくれる- 非同期で取得したデータは state に格納する
useEffect
で state を更新する時に第二引数に空の配列を渡さないと無限ループになる- ダイナミックインポートは
import()
を使ったインポートの方法で、モジュールが必要になったときに読み込めるようになる import()
は Promise を返す- React でダイナミックインポートを使う時は
lazy()
を使って、コールバック関数にコンポーネントを読み込むimport()
を使う
17 章 Next.js 14 (App Router)を使ったアプリの構築方法
- 新規のプロジェクトでは App Router で実装する
- 現在はサーバーのキャッシュを使った SSR が主流
- App Router は
page.js
はリクエストが送信された時に表示するコンポーネントを書くRootLayout
は全てのレイアウトに適用されるlayout.js
がまず読み込まれて、children
にpage.js
のコンポーネントが読み込まれる- パスが見つからない場合は
not-found.js
が読みこまれる - App Router ではフックがある場合は
use client
を書く必要がある - サーバー側で実行するための
use server
は基本は使わない - Next.js では基本的にはサーバー側で完結させる
- クライアント側でレンダリングする部分はなるべく分ける
- サーバーコンポーネントでは直接データを取得できる
- Next.js の
fetch
はブラウザ標準のものとは別で、Next.js 独自の関数 - Next.js では
fetch
でデフォルトでキャッシュを使う generateStaticParams
はダイナミックルーティングに当てはまる値を決めるメソッド- SG で生成されたファイルは
out/
に出力されるので、out
をサーバーに配置すれば公開できる - ページのメタデータは
layout.js
のmetadata
のオブジェクトで設定できる - メタデータを動的にする場合は
generateMetadata()
を使う - Next.js で API を作る時は
api/
にroute.js
を作り、HTTP メソッドごとに関数を作る - Server Actions は REST API 経由でのサーバー側のデータの更新を直接関数経由で行う
- Server Actions の関数には
use server
を記述する useFormState
は新しい React のフックで、Server Actions の結果を画面に表示することができる
18 章 Next.js 12 (Pages Router)基本的な使い方
next.config.js
は Next.js の設定を書く- ディレクトリかファイル名に
[]
を使うことで、動的なルーティングを実装できる - ページが
index.js
だと URL で省略できる - 動的なルーティングと固定のルーティングでは固定のルーティングが優先される
- ダイナミックルーティングは 1 つの階層に 1 つしか作れない
getServerSideProps
は SSR を行うための関数- 動的ルーティングの値を取得するには、
context.query
またはuseRouter
のrouter.query
を使う useRouter
が返すオブジェクトは URL のパスの情報や画面遷移のメソッドの情報が格納されているrouter.push
で画面遷移できるrouter.replace
でも画面遷移できるが、履歴を上書きするrouter.back
で 1 つ前の画面に戻るrouter.reload
で画面を更新するLink
コンポーネントを使わずに a タグを使うと画面が更新される- クエリパラメータは文字列で取得される
next.config.js
でrewrites
を設定することで実際の URL とは異なる URL でアクセスできる_app.js
はページを開いた時に必ず実行されるコンポーネントなので、グローバルな state を使う時は_app.js
で Provider を使うHead
は head タグに挿入するタグを書くScript
コンポーネントは外部スクリプトを読み込み時に使うが、ほぼ使わないpages
に外部からアクセスされてはいけないファイルは置かない- 静的なリンクには
Link
を、何らかの処理をきっかけに画面遷移を行う場合はuseRouter
を使う - Next.js では CSR、SSR、SG、ISR のレンダリング方法があるが、SSR と SG がよく使われる
- CSR はクライアントサイドレンダリングで全ての処理がクライアント上で行われる
- Next.js でクライアント側での処理のみを行いたい時は
useEffect
を使う - SSR はサーバーサイドレンダリングでサーバー側で動的に HTML を生成する
- SSR は外部 API へのデータ取得やコンポーネントの props の値を決定する処理を行い、HTML を作成してクライアント側に返却する
- SG は静的サイト生成で、リクエスト時ではなくビルド時にデータを取得し HTML を生成する
- Next.js では基本的なページは SG で作り、動的な変更が多い場合は SSR で作る
- ISR はビルド時に HTML を構築し、一定時間後のアクセスでサーバー側で HTML を更新する
- ISR はサーバーの設定が手間であるデメリットがある
- Next.js では Node.js 上とブラウザで関数コンポーネントが実行される
- Next.js では仮想 DOM を使って擬似的に Node.js 上で DOM ツリーを作成し、値を代入して HTML を返す
- SSR ではブラウザの window などは使えないので使いたい場合は
useEffect
を使う - SSR は初期表示で、画面遷移では CSR
getServerSideProps
は Node.js 上で実行されるので、コンソールには表示されないgetServerSideProps
はページコンポーネントでしか使えないgetServerSideProps
の引数のcontext
には通信の情報が格納される- Next.js では他の画面から遷移してきた場合は CSR になるが、ページコンポーネントで使用する
pageProps
は Node.js から JSON 形式で受け取る - SG は HTML を出力するもの
- SG はいくつかパターンがある
- export - ビルドしたものを HTML として出力する
getStaticProps
はビルド時に実行され、戻り値が json で保存される- SG ではあらかじめファイルを生成するので、ダイナミックルーティングでの値を取得できない
- SG でダイナミックルーティングを使う時は
getStaticPaths
を使う getStaticPaths
はビルド時に実行され、動的なパスを事前に生成するために使うgetStaticPaths
のparams
はgetStaticProps
のcontext
で取得できるnpm export
だとout/
に静的ファイルを出力するnpm start
はfallback
はfalse
にするとnpm start
を実行した時にgetStaticPaths
に定義されていないパスにアクセスした時に 404 になるISR
は Vercel だとうまく動くが、他のホスティングサービスだと設定が難しいISR
はgetStaticProps
のreturn
にrevalidate
を追加するだけrevalidate
には秒数を指定する- API Routes は Next.js 上で簡易的な API を作る機能
- プロトコル、ドメイン、ポート番号が同一のものが同一オリジン
- クライアントサイドからリクエストを送信する際は外部サービスの API の URL は一般的にはバックエンドで集約して隠蔽する
- Next.js では開発環境には
.env.development
、本番環境では.env.production
に環境変数を設定する - 環境変数を取得する場合は
process.env.
を使う - 環境変数によって実行する環境によって自動的に値が切り替わる
20 章 テスト
- テストがないとコードの改修時に影響範囲を調べる必要がある
- 関連システムを含め、エコシステム全体に対して行うテストを一気通貫テストという
- Jest は初期設定が不要
- Vite のプロジェクトでは Jest に代わり Vitest が使われる(Jest への移行は容易)
it
とtest
は同じ役割- Test Suites はファイルごとで、Tests は 1 つずつのテストを指す
- React での Jest を使ったテストでは、
test
のコールバック関数内でコンポーネントを擬似的にレンダリングして、要素を取得してexpect
を使ってテストを書く - テストで要素を取得する場合は機械的なタグではなく、実際にユーザーが使うアクセシビリティを使った情報や、テキストデータで取得する
- テストコードは Arrange(準備)、Act(実行)、Assertion(結果)の確認の順番で書く
- 更新前もテストすると厳密にテストできる
describe
を使うことでテストをまとめてわかりやすくできるuseReducer
を使うことで関数のテストを書けるuseState
だと、ユーザーの操作による状態の変化をテストする必要がある- オブジェクトを比較する場合は
toEqual
を使う - プリミティブを比較する場合は
toBe
を使う - ユーザーの操作をテストするときには
fireEvent
より忠実に画面の操作に合うように実装されているuserEvent
を使う - 外部 API は通信するたびに取得するデータが変わったり、API のサーバーがダウンしていると、テストが失敗するのでモック化する
- モック化では
jest.mock()
を使う
21 章 TypeScript
- 基本的には関数内で使うオブジェクトは型推論を使い、外部から渡されるオブジェクトは型を定義すべき
- TypeScript では引数の個数が合わないとエラーになる
- 関数コンポーネントの型定義では
React.FC
を使う children
の型はReact.ReactNode
を指定する- TypeScript ではクラスのメンバーを定義する必要がある
- Event の型にはイベントの種類によって変わる
onClick
などに直接関数を記述した場合は自動で型が指定される- 型情報だけをインポートする時は
import type
を使える