Reactカリキュラム
Create Date:2023-03-24 00:11:33
Update Date:2023-03-24 00:11:33

Chapters

React基礎# Reactとは UI(ユーザインターフェース)≒ Web上のアプリケーションの見た目 を構築するための JavaScriptライブラリ コンポーネントと呼ばれる部品を組み合わせてページを構築していく。 例えば、 [google.com](http://google.com) を見てみると以下の部品でページが構築されていることがわかる。 - ヘッダー - 左上の「Googleについて」「ストア」 - 右上の「Gmail」「画像」、9個の点でできた四角いアイコン、ユーザのアイコン - 中央の「Google」のロゴ - 中央の検索ボックス - 虫眼鏡アイコン - 検索したい文字が入力できるエリア - 音声検索用のアイコン(ボタン) - 画像検索用のアイコン(ボタン) - フッター - 所在地(日本) - 左下の「広告」「ビジネス」「検索の仕組み」 - 右下の「プライバシー」「規約」「設定」 こんな感じで、複数のコンポーネントを組み合わせて画面を構築することができるのが React # DOM, 仮想DOM ## DOM について DOM(Document Object Model)は、Webページなどを表現するためのものです。 DOMは、HTMLなどをツリー構造として表現し、それぞれの要素にアクセスすることができます。 DOMを使用すると、JavaScriptを使用して、HTML内の特定の要素を検索して、テキストを変更したり、スタイルを変更したりすることができます。 ## 仮想DOMについて 仮想DOMは、実際のDOMの表現をメモリ内に仮想的に作成することで、より高速なレンダリングとパフォーマンスを実現します。 Reactでは、仮想DOMが、Reactコンポーネント内で定義されたステート(状態)の変化に対して自動的に再描画され、変更が必要な部分だけが実際のDOMに反映されます。 例えば、仮想DOMを使わない場合、リアルタイムで大量のテキストを書き換えるようなアプリケーションだと、大量のDOMノードを操作する必要があり、レンダリングのパフォーマンスが低下することがあります。 しかし、仮想DOMを使うと、Reactがメモリ内に仮想DOMを作成し、必要な変更を仮想DOMに適用し、実際のDOMに反映する際に、必要な変更だけを効率的に適用することができます。 れにより、パフォーマンスが向上し、スムーズなUX(ユーザーエクスペリエンス)が提供されます。 # ライフサイクル・レンダリング 参考リンク: [https://react.dev/learn/render-and-commit](https://react.dev/learn/render-and-commit) Reactのレンダリングは以下の3つのステップに分かれています。 1. レンダリングのトリガー 2. コンポーネントのレンダリング 3. DOMへのコミット Reactのレンダリングをレストランでの例で例えると、以下のようなイメージです。 1. お客さんの注文をキッチンに伝える 2. キッチンで調理 3. 料理をテーブルに届ける それぞれについて詳しく説明します。 ## レンダリングのトリガー コンポーネントがレンダリングされるタイミングは2つあります。 1. コンポーネントの初期レンダリング 1. ページを最初に読み込むときに、最初のレンダリングを行う必要がある 2. 例:レストランのお客さんが最初の注文を行う 2. コンポーネント(もしくはその親)の状態が更新 1. 後で説明する `set関数` で関数や変数の状態を更新することによって、再度レンダリングされる。 2. 例:レストランのお客さんが、喉の渇きやお腹の空き具合によって再度注文する ## コンポーネントのレンダリング レンダリングがトリガーされると、Reactはコンポーネントを呼び出して画面に表示するものを決める。 「レンダリング」= Reactがコンポーネントを呼び出すこと。 1. 最初のレンダリングで、 React はルートコンポーネント(最上位のコンポーネントのこと。≒ HTML の <body>に相当するもの)を呼び出す。 2. Reactによって状態が更新された関数コンポーネントが再度レンダリングされる。 ## DOMへのコミット コンポーネントをレンダリングした(呼び出した)あと、React は DOM を変更する。 1. 最初のレンダリングでは、作成したすべてのDOMのノードを画面に配置する。 2. 再レンダリングでは、Reactは必要最低限の操作(レンダリング中に計算される)によって、DOMを最新のものに更新する
WebアプリケーションのレンダリングとDocker による React の環境構築# Webアプリケーションのレンダリング Webアプリケーションでは、画面にコンテンツを表示するためにいくつかの方法があります。 代表的に、CSR, SSR, SSG, ISR という4種類があります。 以下で、これらの説明をします。 ## 1. CSR(Client-Side Rendering) クライアントサイドレンダリング(CSR)では、すべてのレンダリングがユーザーのブラウザ(クライアント)で行われます。 サーバーからは基本的なHTML骨組みとJavaScriptファイルが送られ、JavaScriptがブラウザ上で実行されてHTMLを生成します。 これにより、ページの内容や構造がブラウザで動的に生成されます。 **例え:** あなたがレストラン(ウェブサイト)に入ると、シェフ(JavaScript)があなたの注文(ユーザーの行動やリクエスト)に基づいて料理(ウェブページ)を作り始めます。 このため、注文を受けてから料理が完成するまで待つ時間が必要です(ページがレンダリングされるまでの待機時間)。 ## 2. SSR(Server-Side Rendering) サーバーサイドレンダリング(SSR)では、ウェブページはサーバー上でレンダリングされ、クライアントには既に生成されたHTMLが送られます。 これにより、ユーザーはページのロードを待つ時間が短縮されます。 しかし、頻繁に更新が必要なページに対しては、都度サーバーでレンダリングを行う必要があるため、サーバーへの負荷が高まる欠点があります。 **例え:** レストラン(ウェブサイト)に入ると、シェフ(サーバー)が事前に料理(ウェブページ)を準備しており、すぐに提供できます。 ただし、毎回注文が入る度にシェフが料理を作り直すため、シェフ(サーバー)への負担が増えます。 ## 3. SSG(Static Site Generation) 静的サイト生成(SSG)では、ビルド時にすべてのページがサーバー上でレンダリングされ、HTMLが生成されます。 これらのHTMLは事前に生成されるため、リクエストがあったときにはすぐに配信できます。 更新の頻度が低く、大量のユーザーに対して同じ内容を配信する場合に適しています。 **例え:** レストラン(ウェブサイト)が開店する前に、シェフ(サーバー)が予め料理(ウェブページ)を作っておきます。 顧客が来たときには、すぐに料理を提供できます。 ただし、メニュー(ウェブページの内容)が変わるたびに、すべての料理を作り直す必要があります。 ## 4. ISR(Incremental Static Regeneration) 増分静的再生成(ISR)は、SSGの概念を拡張したもので、特定のページがリクエストされるとそのページだけを再生成します。 これにより、更新頻度が高いウェブサイトでも、ユーザーに対して高速に静的なページを提供することができます。 **例え:** レストラン(ウェブサイト)が開店する前にシェフ(サーバー)があらかじめ料理(ウェブページ)を作っておきます。 しかし、ある料理(ページ)が注文されたときだけ、その料理を新鮮な状態で提供するために作り直します。 これにより、常に新鮮で最新の料理(ウェブページ)を提供しながらも、全ての料理を毎回作り直す必要がありません。 # create-react-app とは 1つのコマンドでReactのアプリを作成できるコマンド。 簡単に環境構築できるので、アプリ開発に集中できるメリットがある。 # 環境構築 環境構築済みのリポジトリがあるので、ここからクローンしてくる。 クローン方法は README.md に記述しているのでそれを参考に進める。 https://github.com/NUTFes/React-Curriculum/tree/main 手元で一から環境構築したい場合は、以下の`環境構築方法` を参考に進めてください。 <details> <summary>環境構築方法</summary> 参考サイト: https://qiita.com/kurokawa516/items/2be4d40a49bc06d14876 1. 適当なディレクトリを作成 ``` mkdir React-Curriculum cd React-Curriculum ``` 2. docker 関係のファイルを作成 ``` touch Dockerfile touch docker-compose.yml ``` 3. docker 関係のファイルを更新 Dockerfile ```Docker FROM node:18.16.1-alpine WORKDIR /usr/src/app ``` docker-compose.yml ``` version: '3' services: app: build: . volumes: - ./:/usr/src/app command: sh -c 'cd react-app && yarn start' ports: - '3000:3000' ``` 4. build する `docker compose build` でビルドする 5. React アプリの作成 `docker-compose run --rm app sh -c 'npx create-react-app react-app --template typescript` を実行し、 React アプリケーションを作成する。 6. React アプリの起動 `docker compose up` をターミナルで実行し、アプリケーションを起動する。 </details> # できたファイルの説明 1. src 1. 開発用ファイルを格納 2. JSXはここに置く 2. public 1. 画像とか
JSXの基本と記述方法・活用方法# JSXとは 1. JSの拡張言語 2. HTMLっぽいけど、JSの構文が使える # なぜJSXを使うのか 1. reactで書いたものは、JSでしかないから最終的にHTMLに変換する 2. createElementでもreact要素を生成できるしHTML要素に変換できる。でも、毎回書くのが大変 3. だからHTMLライクにかけるJSXの方が良い # JSXの基礎文法 1. 基本的にはHTMLと同じ ```jsx import React from 'react'; function App() { return ( <div> <h1>Hello, World!</h1> <p>This is a React app.</p> </div> ); } export default App; ``` 2. classはclassNameに ```jsx import React from 'react'; function App() { return ( <div className="container"> <h1>Hello, World!</h1> <p className="text">This is a React app.</p> </div> ); } export default App; ``` 3. キャメルケース ```jsx import React from 'react'; function App() { const myName = "John Doe"; return ( <div> <h1>Hello, {myName}!</h1> <p>This is a React app.</p> </div> ); } export default App; ``` 4. {}で変数を扱える ```jsx import React from 'react'; function App() { const myName = "John Doe"; const age = 30; return ( <div> <h1>Hello, {myName}!</h1> <p>You are {age} years old.</p> </div> ); } export default App; ``` 5. returnの中は絶対に1つの要素 1. 最上位(returnの直下)は並列にはできない NG例 ```jsx import React from 'react'; function App() { const myName = "John Doe"; const age = 30; return ( <h1>Hello, {myName}!</h1> <p>You are {age} years old.</p> ); } export default App; ``` OK例 ```jsx import React from 'react'; function App() { const myName = "John Doe"; const age = 30; return ( <div> <h1>Hello, {myName}!</h1> <p>You are {age} years old.</p> </div> ); } export default App; ``` 6. 配列のレンダリング 1. map() で配列をレンダリング 2. key の指定が必須 ```jsx import React from 'react'; function App() { const items = [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' } ]; return ( <ul>" {items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> ); } export default App; ```
ReactにおけるUIの説明# コンポーネントとは 1. コンポーネントとは 1. 見た目と機能を持つUI部品 2. コンポーネントを組み合わせてページを作る 3. クラスコンポーネントと関数コンポーネントがあるけど、最近は関数コンポーネントが主流 1. 関数コンポーネントのほうが記述が少ない 2. Hooksのおかげで、どっちも同じようなことができるようになった 2. なぜコンポーネントを使うのか 1. 再利用するため 2. コードの可読性を上げるため 3. 変更に強くするため # コンポーネントの基本的な使い方 1. インポートとエクスポートによってコンポーネントを使用する 1. 1つのファイルで複数のコンポーネントを宣言できるけど、ファイルが大きいと可読性や保守性が下がる 2. コンポーネントを独自のファイルにエクスポートしてから、そのコンポーネントをインポートする。 ```jsx // Button.tsx import React from 'react'; function Button() { return ( <button>ボタン</button> ); } export default Button; // App.tsx import React from 'react'; import Button from './Button'; function App() { return ( <div> <h1>Hello, World!</h1> <Button /> </div> ); } export default App; ``` 2. ファイル名はアッパーキャメルケース ↑ の Button.tsx, App.tsx というファイル名からわかる 3. 子コンポーネントでexport ```jsx // ChildComponent.js import React from 'react'; function ChildComponent() { return ( <p>Child Component</p> ); } export default ChildComponent; ``` 4. 親コンポーネントでimport ```jsx // ParentComponent.js import React from 'react'; import ChildComponent from './ChildComponent'; function ParentComponent() { return ( <div> <h1>Hello, World!</h1> <ChildComponent /> </div> ); } export default ParentComponent; ``` # コンポーネント間のデータの受け渡し 1. コンポーネント間のデータの受け渡し 1. propsを用いてデータを受け渡す 1. 全部を部品化するってことは、その部品で表示するデータは部品ごとで異なる。 2. だから、その部品を使う側(親コンポーネント)で呼び出してあげる必要がある ```jsx // Button.tsx import React from 'react'; function Button(props) { const { text } = props; return ( <button>{text}</button> ); } export default Button; // App.tsx import React from 'react'; import Button from './Button'; function App() { return ( <div> <h1>Hello, World!</h1> <Button text="Click me!" /> </div> ); } export default App; ```
イベントの処理# クリックで alert を表示 `handleClick()` で `alert` を 表示する。 この `handleClick()` を props で `<Button>` コンポーネントに渡してあげる。 それを、`onClick` として子コンポーネントで受け取り、`button` の `onClick` に渡してあげることでクリック時に `alert` を表示することができる。 ```jsx // Button.tsx import React from 'react'; interface Props { onClick: () => void; text: string; } function Button(props: Props) { const { onClick, text } = props; return ( <button onClick={onClick}>{text}</button> ); } export default Button; // App.tsx import React from 'react'; import Button from './Button'; function App() { const handleClick = () => { alert('Button clicked!'); }; return ( <div> <h1>Hello, World!</h1> <Button onClick={handleClick} text="Click me!" /> </div> ); } export default App; ```
React Hooks の習得# React Hooksとは React 16.8 から使えるようになった機能。 クラスを書かなくても、関数コンポーネントで state などの React の機能を使えるようなもの。 この章では、基本的なHookである `useState` , `useEffect` , `useContext` の3種類の説明を行う。 他にも、 `useMemo` , `useCallback` , `useRef` , `useReducer` などがあるが、今回は説明を省略する。 # useState コンポーネントの state を保持したり更新したりするためのフックで、フックの中でも(多分)1番使う。 state とは、フォームに入力された値など、アプリが保持している状態(≒値、変数などのイメージ)のこと。 例えば、アプリ内で変更される値を管理したい時に使われる。具体的な例だと、入力フォームを実行する時に使用される。 ```jsx const [stateの変数, stateを更新する関数] = useState(stateの初期値) // 型を指定する場合 const [stateの変数, stateを更新する関数] = useState<stateの型>(stateの初期値) // イメージ const [name, setName] = useState('taro') const [name, setName] = useState<string>('taro') ``` ## 具体的な使い方 ```jsx import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); function handleClick() { setCount(count + 1); } return ( <button onClick={handleClick}> You pressed me {count} times </button> ); } ``` ## 状態(state) の更新 ### tips 1. setState() してすぐだと値が更新されてない 2. スプレッド構文(…) によるオブジェクトのコピーと更新 ```jsx import { useState } from 'react'; export default function Form() { const [form, setForm] = useState({ firstName: 'Barbara', lastName: 'Hepworth', email: '[email protected]', }); return ( <> <label> First name: <input value={form.firstName} onChange={e => { setForm({ ...form, firstName: e.target.value }); }} /> </label> <label> Last name: <input value={form.lastName} onChange={e => { setForm({ ...form, lastName: e.target.value }); }} /> </label> <label> Email: <input value={form.email} onChange={e => { setForm({ ...form, email: e.target.value }); }} /> </label> <p> {form.firstName} {form.lastName} {form.email} </p> </> ); } ``` 3. 配列の更新 ```jsx function handleToggle(artworkId, nextSeen) { setList(list.map(artwork => { if (artwork.id === artworkId) { return { ...artwork, seen: nextSeen }; } else { return artwork; } })); } ``` # useEffect useEffectは、関数コンポーネントで副作用を制御できるフック。 副作用とは、DOMの変更やファイルへの書き込みなど、関数の外に影響を与える処理のこと。useEffectを使用すると、関数を実行するタイミングをReactのレンダリング後まで遅らせることが可能になる。 useEffectでは、マウント時に行った処理をアンマウント時に解除する。 副作用関数はレンダリング時に毎回実行される仕組みです。そして、次の副作用関数を実行する前にクリーンアップ関数を返し、一つ前の副作用関数を解除。 この一連の繰り返し処理はライフサイクルと呼ばれる。 useEffectの基本的な記述方法は次の通り ```jsx useEffect(() => { // 副作用処理を記述 return () => { // クリーンアップ処理を記述 }; }, [副作用関数の実行タイミングを制御する依存配列]); ``` useEffectが必要なケースとして、コンポーネントの呼び出し時に外部APIからリソースを取得する場合などが挙げられる。 # useContext コンテクストオブジェクト(`React.createContext` からの戻り値)を受け取り、そのコンテクストの現在値を返す。 コンテクストの現在値は、ツリー内でこのフックを呼んだコンポーネントの直近にある  `<MyContext.Provider>`  の  `value`  の値によって決定される。 `const value = useContext(MyContext);` 直近の `<MyContext.Provider>` が更新された場合、このフックはその `MyContext` プロバイダに渡された最新の `value` の値を使って再レンダーを発生させます。祖先コンポーネントが  [React.memo](<https://ja.reactjs.org/docs/react-api.html#reactmemo>)  や  [shouldComponentUpdate](<https://ja.reactjs.org/docs/react-component.html#shouldcomponentupdate>)  を使っている場合でも、`useContext` を使っているコンポーネント自体から再レンダーが発生します。 `useContext` に渡す引数は**コンテクストオブジェクト自体**であることを忘れないでください。 - **正しい:**`useContext(MyContext)` - **間違い:**`useContext(MyContext.Consumer)` - **間違い:**`useContext(MyContext.Provider)` `useContext` を呼び出すコンポーネントはコンテクストの値が変化するたびに毎回再レンダーされます。再レンダーが高価である場合は[メモ化を使って最適化](https://github.com/facebook/react/issues/15156#issuecomment-474590693)が可能です。 # その他のHooks 上記のHooksの他にも、 `useCallback`, `useMemo`, `useRef` などがありますが、今回は説明を省略します。