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` などがありますが、今回は説明を省略します。