【図解解説】これ1本12分でReactのコンセプト全20種を理解できる教科書

![react-concept-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/9399f69a-fe9e-45b7-95f0-3635562448ea.png)


# はじめに

こんにちは、Watanabe Jin([@Sicut_study](https://x.com/Sicut_study))です。

ReactはAIブームなども相まって人気のJavaScriptフレームワークです。今後も更にReactを利用したアプリケーションは増えることが想像できます。

Reactのコンセプト(基礎)をしっかりと理解することは、効率的で保守性の高いアプリケーション開発において不可欠です。今回はそのなかでも特に大切なものを20個解説していきます。

この記事を読むことで

- Reactの基礎から応用までの体系的な知識を一箇所で習得できる
- コンポーネント設計やステート管理など、直面する課題に対する解決策を理解できる
- パフォーマンス最適化やエラー処理などアプリケーション開発に必要な知識を得られる
- 最新機能（React 19、Suspenseなど）について学び取り入れられる
- 面接や技術的な議論の場で、Reactについて自信を持って説明できるようになる

このような力を身につけることができるのではないかと思います。
私自信もしっかりと基礎を理解してからReactを触ることで成長が加速しました。

初心者の方には体系的な学習の道筋を、中級者・上級者の方には知識の整理と深堀りの機会になるかと思います。

# 動画教材も用意しています
こちらの教材にはより詳しく解説した動画もセットでご用意しています。
テキスト教材でわからない細かい箇所があれば動画も活用ください

<iframe width="560" height="315" src="https://www.youtube.com/embed/Sszritc6WOc?si=EDJqk-NUmr6OLSV_" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# 対象者
- Reactを勉強し始めた方
- もっとReactを深く理解したい方
- 短時間で学びたい方
- 基礎を固めたい方

# 1. Component

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/055604a2-fd9b-4f0f-93f4-b9853c01e102.png)

Reactの中核となるのが「コンポーネント」です。
レゴブロックのようにUI(ボタンやフォーム)を組み合わせることによってページを作成していきます。

この再利用可能な独立したUIを**コンポーネント**といいます。

コンポーネントを使うことで以下のメリットがあります。

**再利用性** : 一度作れば何度でも使い回せる
**保守性** : UIを独立した部品に分割することでコードの管理が簡単
**テスト容易性** : 小さく分離されていることで個別にテストしやすくなる

コンポーネントには2種類あります。

### 関数コンポーネント

```jsx
function Greeting(props) {
  return <h1>こんにちは、{props.name}さん！</h1>;
}
```

### クラスコンポーネント

```jsx
class Greeting extends React.Component {
  render() {
    return <h1>こんにちは、{this.props.name}さん！</h1>;
  }
}
```

:::note warn
ただし現在のReactでは関数コンポーネントが推奨されています
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/f43370c0-c0a6-471c-a1ba-acf0b0c6fb0e.png)

この例であれば`<Greeting />`でコンポーネントを利用できます。
`App`自体もコンポーネントとなっています。

# 2. JSX

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/d689f471-9d1c-453f-91d4-087f5c2c3024.png)

JSXは<font color="red">**J**</font>ava<font color="red">**S**</font>cript<font color="red">**X**</font>MLの略称でHTMLをJavaScriptコードの中で直接記述できる**シンタックスシュガー**(ある構文を別の記法で記述できるようにしたもの)です。

```jsx
function Greeting() {
  return <div>こんにちは</div>
}
```

このように書いてもブラウザでは認識することができません。
これはJavaScriptをわかりやすく書くためにJSXという直感的に書ける記法を用いて書いています。

実際にはJSXが内部でコンパイルされて**JavaScriptに変換**されています。

```js
function Greeting() {
  return React.createElement(
    'div',
    null,
    'こんにちは、世界！'
  );
}
```

JSXは最終的にReact.createElementの呼び出しに変換されます。
しかしこれでは直感的に開発するのが難しいのでJSXというわかりやすい形で書いています。(もちろんこの形式でjsファイルに書いてもReactは利用可能です)

**JSXにはいくつかのルールがあります**

### 1. 単一のルート要素を返す

JSXでは、コンポーネントが返す要素は必ず単一のルート要素で囲む必要があります。

```jsx
// 正しい例
function Component() {
  return (
    <div>
      <h1>タイトル</h1>
      <p>内容</p>
    </div>
  );
}

// 間違った例
function Component() {
  return (
    <h1>タイトル</h1>
    <p>内容</p>
  );
}
```

### 2. すべてのタグを閉じる

HTMLのような見た目ですが、XMLの構文ルールに従います。そのため、すべてのタグを閉じる必要があります。

```jsx
// 正しい例
<img src="image.jpg" alt="画像" />
<input type="text" />
<br />

// 間違った例
<img src="image.jpg" alt="画像">
<input type="text">
<br>
```

### 3. (ほぼ)属性はキャメルケース

HTMLとJSXの大きな違いの一つは、属性の命名規則です。JSXでは、ほとんどの属性はキャメルケース（小文字で始まり、複合語の各単語の先頭を大文字にする記法）で書きます。

```jsx
// HTML: <div class="container" tabindex="0" onclick="handleClick()">
// JSX:  <div className="container" tabIndex={0} onClick={handleClick}>
```

JSXの場合`class`ではなく`className`を使います。

# 3. カーリーブレス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/6120560e-f2de-479b-b5de-096bbc38aa82.png)

カーリーブレスを利用することで条件付きレンダリングも可能です。

```jsx
const isLoggedIn = true;
const element = (
  <div>
    {isLoggedIn ? <button>ログアウト</button> : <button>ログイン</button>}
  </div>
);
```

isLoggedInの値によって表示する画面を切り替えています。

# 4. Props

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/c491e231-da69-4216-8c2b-edac9d3277f2.png)

propsは「Properties（プロパティ）」の略で、Reactコンポーネントに渡すデータのことです。

### Propsの渡し方

親コンポーネントから子コンポーネントにpropsを渡すには、HTMLの属性のような構文を使います

```jsx
function App() {
  return (
    <div>
      <Welcome name="太郎" age={25} isAdmin={true} />
    </div>
  );
}
```

### Propsの受け取り方

```jsx
function Welcome(props) {
  return (
    <div>
      <h1>こんにちは、{props.name}さん！</h1>
      <p>あなたは{props.age}歳です。</p>
      <p>{props.isAdmin ? '管理者権限があります' : '一般ユーザーです'}</p>
    </div>
  );
}
```

または分割代入で受け取ることもできます。

```jsx
function Welcome({ name, age, isAdmin }) {
  return (
    <div>
      <h1>こんにちは、{name}さん！</h1>
      <p>あなたは{age}歳です。</p>
      <p>{isAdmin ? '管理者権限があります' : '一般ユーザーです'}</p>
    </div>
  );
}
```

# 5. Children

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/e95c8a95-97e9-4a77-8b29-afc4a8081237.png)

コンポーネントの開始タグと終了タグの間に記述された内容を参照するための特別なPropsです。

childrenを活用することで以下のことが実現可能です。

```jsx
function Card({ title, children }) {
  return (
    <div className="card">
      <div className="card-header">{title}</div>
      <div className="card-body">{children}</div>
    </div>
  );
}

<Card title="ユーザー情報">
  <p>名前: 山田太郎</p>
  <p>職業: エンジニア</p>
</Card>
```

:::note warn
ただしChildrenの使用は一般的ではないのでコードが壊れやすくなる可能性があります
:::

# 6. Key Props

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/1e4d6983-8411-41b8-9af3-419284213374.png)

key propはReactのリスト要素に対して設定する特別な属性です。Reactがリスト内の各要素を一意に識別し、効率的な再レンダリングを行うために使用されます。

### 1. 効率的な更新をする

Reactは変更があった部分だけを効率的に更新します。
たとえばリストの順序を変える例を考えてみます。

```
<ul>
  <li>アイテム1</li>
  <li>アイテム2</li>
</ul>

// リストの先頭に新しい項目を追加
<ul>
  <li>新しいアイテム</li> // 新しい項目
  <li>アイテム1</li>      // 「アイテム1」だったDOM要素が更新される
  <li>アイテム2</li>      // 「アイテム2」だったDOM要素が更新される
</ul>
```

このとき、Reactは全ての項目が変更されたと判断し、全てを再描画します。
ここでKeyがある場合は

```
<ul>
  <li key="item1">アイテム1</li>
  <li key="item2">アイテム2</li>
</ul>

// リストの先頭に新しい項目を追加
<ul>
  <li key="new-item">新しいアイテム</li> // 新しいDOM要素が作成される
  <li key="item1">アイテム1</li>          // 既存のDOM要素がそのまま使われる
  <li key="item2">アイテム2</li>          // 既存のDOM要素がそのまま使われる
</ul>
```

必要な部分だけを更新することが可能です。

### コンポーネントの状態維持

keyはコンポーネントのアイデンティティを決定します。同じkeyを持つコンポーネントはReactによって同じコンポーネントと認識され、状態が維持されます。keyが変わると、Reactはそのコンポーネントを破棄して新しいコンポーネントを作成します。

```jsx
function App() {
  const [items, setItems] = useState(['アイテム1', 'アイテム2']);
  
  return (
    <ul>
      {items.map((item, index) => (
        <ItemWithState key={item} text={item} />
      ))}
    </ul>
  );
}

function ItemWithState({ text }) {
  const [isChecked, setIsChecked] = useState(false);
  
  return (
    <li>
      <input
        type="checkbox"
        checked={isChecked}
        onChange={() => setIsChecked(!isChecked)}
      />
      {text}
    </li>
  );
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/af4f61fb-46c0-4c31-9109-8f1d2ef23694.png)

この例では、各ItemWithStateコンポーネントは自身のチェックボックスの状態を持っています。適切なkeyがあれば、リストの項目が並べ替えられても、各チェックボックスの状態は対応する項目に紐付いたままになります。

**Keyはリストで一意性がありレンダリングされても変わらない安定した値をしようしましょう**

# 7. レンダリング

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/c8b8bfc7-21e9-40d2-a3ce-df981a92a2ee.png)

レンダリングとはコンポーネントがReactによって実行され、画面に表示される要素を決定するプロセスのことです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/86199961-8949-42a5-bc42-1ae2073fe77a.png)

**仮想DOM**は、実際のブラウザDOMの軽量な JavaScript表現です。Reactがメモリ上に保持する単なるJavaScriptオブジェクトであり、実際のDOMの状態のコピーのようなものです。

レンダリングは以下のプロセスで行われます。

**1. レンダーフェーズ**

コンポーネントがレンダリングされると、Reactはコンポーネントから仮想DOM要素を作成します。

1.JSX記法がReact.createElement()の呼び出しに変換される
2.render()メソッドや関数コンポーネントの本体が実行される
3.Reactは新しい仮想DOMツリーを構築する

**2. 調整フェーズ**
新しく生成された仮想DOMと前回のレンダリング結果(DOM)を比較し、実際に変更が必要な箇所を特定します。

1.異なる型の要素は完全に再構築される
2.同じ型の要素はプロパティのみが更新される
3.key属性を使って子要素の追加・削除・移動を効率的に検出する

**3. コミットフェーズ**
実際のDOM操作を行われます。

1.前のフェーズで特定された変更のみを実際のDOMに適用
2.副作用（useEffectなど）の実行

仮想DOMを利用することで変更箇所だけを効率的に更新ができる利点があります。

再レンダリングのトリガーには4つあります。

### 1.状態(State)の変更

```jsx
const [count, setCount] = useState(0);
// setCount(1) を実行するとコンポーネントが再レンダリング
```

### 2.プロパティ(props)の変更

```jsx
// Parent component:
<Child name={userName} />
// userNameが変わると、Childコンポーネントが再レンダリング
```

### 3.親コンポーネントの再レンダリング

```jsx
function Parent() {
  const [count, setCount] = useState(0);
  // countが変わると、ChildコンポーネントもProps変更がなくても再レンダリング
  return (
    <div>
      <Child />
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}
```

### 4.コンテキスト(Context)の変更

```jsx
const value = useContext(ThemeContext);
// ThemeContextの値が変更されると再レンダリング
```

不要なレンダリングを減らすために`useCallback`や`useMemo`などを利用してレンダリングを最適化することも可能です。

# 8. イベントハンドラ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/1d8a8621-4a7a-4614-9bd7-dfca17cb6ef8.png)


ボタンをクリックしたり、フォームを入力したりWebアプリには様々なユーザー操作があります。
ユーザー操作が行われたらそれに反応して何かしらのフィードバックを返すために利用するのがイベントハンドリングです。

```jsx
function Button() {
  function handleClick() {
    alert('ボタンがクリックされました！');
  }

  return (
    <button onClick={handleClick}>
      クリックしてください
    </button>
  );
}
```

ボタンをクリックした時に関数を実行するには`onClick`が利用できます。
クリックをすると`handleClick`関数が実行されます。

HTMLで普通に書くとイベントハンドラは小文字ですが、Reactではキャメルケースで記述します。onclickではなくonClickのように書きます。

他にもReactには様々なイベントハンドラが用意されています。

```jsx
// フォームの入力変更を検知
function InputForm() {
  function handleChange(event) {
    console.log('入力値:', event.target.value);
  }

  return <input onChange={handleChange} />;
}

// フォーム送信時
function Form() {
  function handleSubmit(event) {
    // フォームのデフォルト送信動作を防止
    event.preventDefault();
    console.log('フォームが送信されました');
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" />
      <button type="submit">送信</button>
    </form>
  );
}
```

またイベントハンドラは簡単なものであればこのように書くことも可能です。

```jsx
function App() {
  return (
    <button onClick={() => alert('クリックされました！')}>
      クリック
    </button>
  );
}
```

# 9. ステート

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/0a3c597a-b8b6-4472-983a-d3f742c5ca91.png)

stateはReactコンポーネントが内部で管理する可変データです。
stateが変更されると、Reactはコンポーネントを再レンダリングし、変更を画面に反映します。これにより、動的なユーザーインターフェースを構築できます。

初心者がやってしまう間違いが以下のようなコードです。

```jsx
function Counter() {
  // 通常の変数として宣言
  let count = 0;

  function handleClick() {
    // letで宣言した変数を更新
    count = count + 1;
    console.log('現在のカウント:', count); // 値は増えているがUIは更新されない
  }

  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={handleClick}>増やす</button>
    </div>
  );
}
```

このコードではcountの値は変更されていますが画面には反映されません。
これはステートを利用していないことが理由です。

```jsx
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>増やす</button>
    </div>
  );
}
```

このようにステートを利用することで再レンダリングが起きて画面に反映されます。

ステートの使い方は簡単です。

```jsx
const [count, setCount] = useState(0);
```

useSateというHooksを利用します。0は初期値です。
countに実際のステートの値、setCountはステートの値を更新する関数です。

```jsx
setCount(count + 1)
```

setCountに値をいれることでステートの更新をすることができます。(つまり画面が再レンダリングされる)

# 10. Controlled Component

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/37e55d04-ed08-4cc8-8d02-9c6c84395f93.png)


HTMLのフォーム要素（input、textarea、selectなど）は通常、自身で状態を保持し更新します。しかし、Reactではstateが「信頼できる唯一の情報源」であるべきという考え方があります。

https://ja.react.dev/learn/sharing-state-between-components

Controlled Componentでは
1.フォーム要素の値はReactのstateによって制御される
2.入力値の変更はイベントハンドラを通じてstateを更新する
3.表示される値は常にReactのstateから取得される

これらの原則によって、Reactコンポーネントはフォーム要素の動作を完全に制御できるようにデザインされています。

```jsx
function InputForm() {
  const [inputValue, setInputValue] = useState('');
  
  function handleChange(event) {
    setInputValue(event.target.value);
  }
  
  return (
    <div>
      <input
        type="text"
        value={inputValue} // stateを値として設定
        onChange={handleChange} // 変更時にstateを更新
      />
      <p>入力された値: {inputValue}</p>
    </div>
  );
}
```

Controlled Componentのメリットは入力した値を簡単にカスタマイズできることです。
例えばバリデーションをするとこのようになります。

```jsx
function EmailInput() {
  const [email, setEmail] = useState('');
  const [isValid, setIsValid] = useState(true);
  
  function validateEmail(email) {
    const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return re.test(email);
  }
  
  function handleChange(event) {
    const newEmail = event.target.value;
    setEmail(newEmail);
    setIsValid(validateEmail(newEmail) || newEmail === '');
  }
  
  return (
    <div>
      <input
        value={email}
        onChange={handleChange}
      />
      {!isValid && <p style={{ color: 'red' }}>有効なメールアドレスを入力してください</p>}
    </div>
  );
}
```

メールアドレスの入力が始まると`handleChange`が動きます。

```jsx
 function handleChange(event) {
    const newEmail = event.target.value;
    setEmail(newEmail);
    setIsValid(validateEmail(newEmail) || newEmail === '');
  }
```

ここでは`setIsValid`の中で`validateEmail`が実行されてリアルタイムでメールアドレスが有効化をチェックしています。

```jsx
  function validateEmail(email) {
    const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return re.test(email);
  }
```

入力値に対して即座に反応する必要があるUIを構築する際にControlled Componentは有効です。

# 11. Hooks

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/669df602-b716-4d8f-ae24-d5b9cefa9e26.png)

stateやライフサイクルメソッドなどのReactの機能を関数コンポーネントで使えるようにする仕組みです。
Hooksを使うことで、コードの再利用性が高まり、コンポーネントの複雑さが軽減され、関連する機能をまとめやすくなります。

こちらに関してはすべて学べる教材を作成していますのでぜひご覧ください👇

https://qiita.com/Sicut_study/items/d4778cbe8b499570f79e

# 12. 純粋性

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/a4aaec4d-fa7e-41e0-828c-37c1a8b6beb8.png)

Reactにおける純粋性は、関数型プログラミングの重要な概念の一つで、コンポーネントの予測可能性と信頼性を高める原則です。

純粋関数とは、以下の特性を持つ関数のことです。

### 1. 同じ入力に対して常に同じ出力を返す

以下は純粋な関数の例です。

```js
function add(a, b) {
  return a + b;
}
```

常にaとbが決まれば同じ値が関数から返ってきます。

### 2. 副作用がない

関数は外部の状態を変更せず、関数の実行は入力と出力のみに依存します

以下は純粋な関数ではありません。

```js
let total = 0;

function addToTotal(value) {
  total += value;
  return total;
}
```

呼び出す回数によって返却値が変わってしまいます。
純粋性があることによって「パフォーマンス最適化」「バグの減少」などにつながります。

# 13. Strict Mode

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/94462997-e6cc-408e-801b-be23f3e3a6cf.png)

潜在的な問題を早期に発見するための開発ツールです。本番環境では追加の動作をせず、開発時のみ動作する特別なモードです。

Strict Modeを利用することで「問題の早期発見」「非推奨なAPIの使用警告」「よりよい実装パターンの提案」が可能になっています。

`<StrictMode>`タグで囲むことで利用することができます。

```jsx
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/4f128294-55bc-4616-b438-8e8c9ec395d8.png)

Strict Modeでは意図的に2回実行をするので初心者のコンソールが2回出てると思うあるあるが起きます。

# 14. 副作用

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/944dc442-a5e7-4913-8a46-5e0dacc67e51.png)

副作用とは、コンポーネントの表示（レンダリング）以外で行われる処理のことです。

- データの取得（API呼び出し、データベースの操作）
- タイマーの設定（setTimeout, setIntervalなど）
- イベントリスナーの登録/削除
- Cookieやローカルストレージへのアクセス
- 外部サービスとの連携

これらは副作用が発生する代表的な例です。
Reactは「純粋」でなければならないためレンダリング中には副作用を発生させてはいけないので、別に副作用を管理する必要があるのです。

`useEffect`は副作用を管理する代表的なHooksです。

```jsx
import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  // データ取得の副作用
  useEffect(() => {
    setLoading(true);
    
    fetch(`https://api.example.com/users/${userId}`)
      .then(response => response.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      })
      .catch(error => {
        console.error('Error fetching user:', error);
        setLoading(false);
      });
  }, [userId]); // userIdが変わったときだけ実行

  if (loading) return <div>Loading...</div>;
  if (!user) return <div>User not found</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
    </div>
  );
}
```

取得できるデータは常に同じとは限らないため純粋性がなく、`useEffect`の中で取得しています。
※ データ取得はuseEffectの中ではやらないほうがいいのでSWRやReactQueryを利用しましょう

# 15. Refs

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/531c97e2-af5a-4595-ba02-11a76ed187c2.png)

Refsは、ReactでDOMノードや React要素に直接アクセスするための方法です。
詳しくは以下の記事で解説しています👇

https://qiita.com/Sicut_study/items/d4778cbe8b499570f79e

# 16. コンテキスト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/c96894bd-8924-49f8-8bf8-05fd23f21efb.png)

Reactコンポーネントツリーを通じてデータを直接渡す方法を提供します。通常のReactアプリケーションでは、データは親から子へpropsを通して「トップダウン」に渡されますが、コンテキストを使うと、中間コンポーネントを経由せずにデータを共有できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/9a059a1a-6b83-41b8-886d-148302558e9b.png)

コンポーネントが深くネストされた状況で、多くのコンポーネントに同じデータを渡す必要がある場合（テーマ、ユーザー情報など）、props経由でデータを渡していくと「Propsのバケツリレー」が発生します。コンテキストはこの問題を解決します。

# 17. Portals

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/f0f4402c-867e-4055-bdea-360145071fd9.png)

Portalは、親コンポーネントのDOM階層の外側にある別のDOM要素にコンポーネントをレンダリングするための機能です。通常、最も近い親ノードのDOM階層内にレンダリングされますが、ポータルを使うと階層外の任意の場所に要素を「転送」できます。

```jsx
import { createPortal } from 'react-dom';

function Modal({ children }) {
  const modalRoot = document.getElementById('modal-root');
  
  // createPortal(レンダリングする内容, 実際にレンダリングされるDOM要素)
  return createPortal(
    <div className="modal">
      {children}
    </div>,
    modalRoot
  );
}
```

実際にHTMLではコンポーネントを表示したい箇所にidを振ってあげます

```html:index.html
<div id="root"></div>
<div id="modal-root"></div> <!-- ポータルの転送先 -->
```

特定のUIコンポーネント（モーダル、ツールチップ、通知など）を実装する際に非常に便利なツールです。
ただし通常のフローから外れるため、必要な場合にのみ使用してください。

# 18. Suspense

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/a48e86d0-ebd6-4601-89f3-186aca8046c9.png)

Suspenseは、コンポーネントがレンダリングを一時停止して、何らかのリソース取得が完了するのを待つための機能です。非同期操作（データ取得など）の間にローディング状態を代わりに表示できます。

ReactQueryやSWRなどSuspenseに対応しているライブラリを利用することで、データ取得時にローディングを出すことができます。

```jsx
import { QueryClient, QueryClientProvider, useQuery } from 'react-query';
import { Suspense } from 'react';

const queryClient = new QueryClient();

function UserData({ userId }) {
  const { data } = useQuery(
    ['user', userId], 
    () => fetchUser(userId),
    { suspense: true }
  );

  return <div>ユーザー名: {data.name}</div>;
}

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <h1>ユーザープロフィール</h1>
      
      <Suspense fallback={<div>ユーザーデータを読み込み中...</div>}>
        <UserData userId={1} />
      </Suspense>
    </QueryClientProvider>
  );
}
```

React 18ではstartTransitionやuseTransitionといったAPIが導入され、Suspenseと組み合わせることで、ユーザー体験を向上させることができます

```jsx
const ProfileTab = lazy(() => {
  return new Promise(resolve => {
    setTimeout(() => resolve(import('./ProfileTab')), 1500);
  });
});

const SettingsTab = lazy(() => {
  return new Promise(resolve => {
    setTimeout(() => resolve(import('./SettingsTab')), 1500);
  });
});

function App() {
  const [activeTab, setActiveTab] = useState('home');
  const [isPending, startTransition] = useTransition();

  function handleTabChange(tab) {
    startTransition(() => {
      setActiveTab(tab);
    });
  }

  return (
    <div className="app">
      <div className="tabs">
        <button 
          onClick={() => handleTabChange('home')}
          className={activeTab === 'home' ? 'active' : ''}
        >
          ホーム
        </button>
        <button 
          onClick={() => handleTabChange('profile')}
          className={activeTab === 'profile' ? 'active' : ''}
        >
          プロフィール
        </button>
        <button 
          onClick={() => handleTabChange('settings')}
          className={activeTab === 'settings' ? 'active' : ''}
        >
          設定
        </button>
      </div>

      {isPending && <div className="loading-indicator">切り替え中...</div>}
      
      <div className="tab-content">
        <Suspense fallback={<div className="loading">コンテンツ読み込み中...</div>}>
          {activeTab === 'home' && <div>ホームコンテンツ</div>}
          {activeTab === 'profile' && <ProfileTab />}
          {activeTab === 'settings' && <SettingsTab />}
        </Suspense>
      </div>
    </div>
  );
}

export default App;
```

タブを切り替えると(わざと)遅延が発生します。

```jsx
  function handleTabChange(tab) {
    startTransition(() => {
      setActiveTab(tab);
    });
  }
```

まずはタブが切り替えられて`startTransition`が動きます。
**レンダリングが可能**になるまではisPendingが`true`になります。

```jsx
  const [isPending, startTransition] = useTransition();

  (省略)

      {isPending && <div className="loading-indicator">切り替え中...</div>}
```

タブがレンダリング可能になったあとは、実際にレンダリングを行おうとします。
わざと遅延させているのでその間はSuspense状態になり、読み込み画面が変わりに表示されます。

```jsx
const ProfileTab = lazy(() => {
  return new Promise(resolve => {
    setTimeout(() => resolve(import('./ProfileTab')), 1500);
  });
});

(省略)

        <Suspense fallback={<div className="loading">コンテンツ読み込み中...</div>}>
```

Suspenseを使うことでif/elseや条件付きレンダリングの代わりに、Suspenseを使ってローディング状態を宣言的に表現できます。

# 19. Error Boundary

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/58c61159-5373-4ec5-a5a0-41961b721c65.png)

Reactアプリケーション内でJavaScriptエラーをキャッチし、エラーが発生した場合にフォールバックUIを表示するためのReactコンポーネントです。Error Boundaryを使用することで、アプリケーション全体がクラッシュすることなく、エラーが発生した部分だけを適切に処理できます。

```jsx
import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>エラーが発生しました:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>再試行</button>
    </div>
  );
}

function MyComponent() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onReset={() => {
        // リセット時に追加で行いたい処理
      }}
      resetKeys={[/* 変更時にエラー状態をリセットする依存配列 */]}
    >
      <ComponentThatMightError />
    </ErrorBoundary>
  );
}
```

多くのアプリケーションでは、サードパーティのライブラリ「react-error-boundary」を使用しています。

`<ComponentThatMightError>`の中でエラーが発生するとアプリケーション全体がクラッシュしますが、Error Boundaryのコンポーネントで囲んであげることによってChildrenでエラーが出た時に変わりの画面を表示しています。

```jsx
function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <div role="alert">
      <p>エラーが発生しました:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>再試行</button>
    </div>
  );
}
```

# 20. イベント伝播

イベント伝播とは、DOMツリーを通じてイベントが親要素や子要素に伝わる仕組みです。イベント伝播のメカニズムを理解することで、効率的なイベントハンドリングが可能になります。

イベント伝播には2つのフェーズがあります

1.キャプチャリングフェーズ：イベントがルート要素から発生した要素へ降りていく
2.バブリングフェーズ：イベントが発生した要素からルート要素へ上がっていく

```jsx
<div onClick={handleDivClick}> {/* 3. 最後にここへ */}
  <button onClick={handleButtonClick}> {/* 2. 次にここへ */
    <span onClick={handleSpanClick}>クリック</span> {/* 1. ここで最初にクリックが発生 */}
  </button>
</div>
```

この例では、spanをクリックすると、イベントはバブリングにより、spanからbuttonへ、そしてdivへと伝播します。

内側のボタンをクリックしたらすべてのボタンが反応しても困ります。なのでイベントを制御する方法があります。

### 1. バブリングの停止

```jsx
function Button() {
  const handleClick = (e) => {
    e.stopPropagation();
    console.log('ボタンがクリックされました');
    // このイベントは親要素へ伝播しない
  };

  return (
    <div onClick={() => console.log('div がクリックされました')}>
      <button onClick={handleClick}>クリック</button>
    </div>
  );
}
```

`e.stopPropagation`を利用することで止めることができます。

### 2. デフォルト動作の阻止

```jsx
function Form() {
  const handleSubmit = (e) => {
    e.preventDefault(); // フォームの送信を阻止
    // カスタム処理
  };

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">送信</button>
    </form>
  );
}
```

`preventDefault()`で、要素のデフォルト動作を止めることができます。

複雑なコンポーネント階層を持つアプリケーションでは、イベントの伝播とその制御方法を知ることが重要です。

# おわりに

いかがでしたでしょうか？
Reactのコンセプトの中にも知らないことなどあったのではないでしょうか？
基礎を理解することでよりReactを使いこなせるようになるはずです。詳しく解説した動画を投稿しているのでよかったらみてみてください！

<iframe width="560" height="315" src="https://www.youtube.com/embed/Sszritc6WOc?si=EDJqk-NUmr6OLSV_" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

# JISOUのメンバー募集中！
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？
興味のある方は、ぜひホームページからお気軽にカウンセリングをお申し込みください！
▼▼▼

https://projisou.jp

# 図解ハンズオンたくさん投稿しています！

https://qiita.com/Sicut_study/items/1d5c4f2ddac621565ce2

https://qiita.com/Sicut_study/items/ee41f1bb59fcda45a50b

https://qiita.com/Sicut_study/items/d1dd4727881cf4dfd026