【React】React 初心者でも「ステート管理」を完全に理解できる（ライブラリは使わないよ）

# 1.はじめに

私は  
「React ってファイルを行ったり来たりして、わけわからん！」  
「 React めちゃむずい」  
って思っていました。

この React の行ったり来たりの流れを整理するために本記事を書きました。

同じように思われている方のお役に立てれば幸いです。

本記事は、「ステート管理」について見ていきます。

ステート の **超** 基礎的な内容となっています。

※ ステート管理には、`useState` 以外の フック や ライブラリ 等は使用しておりません。

**超** 簡単な内容でどのように state の値が渡っていくのかをみていきたいと思います。

もし間違って認識しているところ等ありましたらご指摘いただけますと幸いです。



# 2.目次

[1.はじめに](#1はじめに)  
[2.目次](#2目次)  
[3.この記事でわかること](#3この記事でわかること)  
[4.環境](#4環境)  
[5.ステート管理](#5ステート管理)  
　[5.1.上から下にステート値を渡す](#51上から下にステート値を渡す)  
　[5.2.下から上にステート値を渡す](#52下から上にステート値を渡す)  
[6.おわりに](#6おわりに)  
[7.参考](#7参考)  




# 3.この記事でわかること

ステートの値が、
- 親コンポーネント → 子コンポーネント → 孫コンポーネント（上から下へステート値を渡す）
- 孫コンポーネント → 子コンポーネント → 親コンポーネント（下から上へステート値を渡す）

という流れを見ていきます。

ルートコンポーネントでステート値を受け取って親・子・孫コンポーネントに値を渡して、ユーザー情報を表示させ、  
孫コンポーネントの削除ボタンをクリックして、子・親コンポーネントへ変更したステート値を渡す流れとなります。

 ↓ こんな感じの簡単なアプリです。

![ステート管理.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/756415/40d5ba9b-3a9a-c795-6a40-784a11cc66f6.gif)

最終的なソースコードは下記です。

型定義の `src/user.ts` や `src/userList.ts`、ユーザー情報の `src/user-data.json` については、本記事では振れません。  
あと、`src/index.tsx` はコンポーネントを表示するためのファイルなので、こちらも説明は省略します。  
上記のソースコードについては、こんな風に定義してるんだな程度で見ていただけますと幸いです。  
（下記をコピーすると動くようにはなっています。）

```ts:src/user.ts（型定義）
export type User = {
  id: string;
  name: string;
  age: number;
  onRemoveUser?: (id: string) => void;
  onRemove?: (id: string) => void;
};
```

```ts:src/userList.ts（型定義）
import type { User } from "./user";

export type UserList = {
  users: User[];
  onRemoveUser?: (id: string) => void;
};
```

```json:src/user-data.json（ユーザー情報）
[
  {
    "id": "dahis1",
    "name": "daishi",
    "age": 10
  },
  {
    "id": "manju2",
    "name": "manju",
    "age": 20
  },
  {
    "id": "daishiman3",
    "name": "daishiman",
    "age": 30
  }
]
```


```tsx:src/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

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

```tsx:src/App.tsx（親コンポーネント）
import { useState } from "react";
import userData from "./user-data.json";
import { UserList } from "./UserList";
import type { User } from "./user";

export default function App() {
  const [users, setUsers] = useState<User[]>(userData);

  const removeUser = (id: string) => {
    const newUsers = users.filter((user) => user.id !== id);
    setUsers(newUsers);
  };

  return (
    <div>
      <UserList users={users} onRemoveUser={removeUser} />
    </div>
  );
}
```

```tsx:src/UserList.tsx（子コンポーネント）
import { FC } from "react";
import { User } from "./User";
import type { UserList as UserListType } from "./userList";

export const UserList: FC<UserListType> = ({
  users = [],
  onRemoveUser = (f) => f
}) => {
  if (!users.length) return <div>表示できるユーザーはいません</div>;

  return (
    <div>
      {users.map((user) => (
        <User key={user.id} {...user} onRemove={onRemoveUser} />
      ))}
    </div>
  );
};
```

```tsx:src/User.tsx（孫コンポーネント）
import { FC } from "react";
import type { User as UserType } from "./user";

export const User: FC<UserType> = ({ 
  id,
  name,
  age,
  onRemove = (f) => f
}) => {
  return (
    <section>
      <p>
        {name}（年齢：{age}歳）
      </p>
      <button onClick={() => onRemove(id)}>削除</button>
    </section>
  );
};
```


# 4.環境

- react: 17.0.2
- typescript: 4.4.2

# 5.ステート管理

ステートの値の管理をユーザー情報を表示したり、削除する簡単な流れを見ていきます。

:::note info

1. **親**コンポーネント → **子**コンポーネント → **孫**コンポーネント
    （上から下へステート値を渡す）
2. **孫**コンポーネント → **子**コンポーネント → **親**コンポーネント
    （下から上へステート値を渡す）

:::

という流れで見ていきます。

1 . **親**コンポーネント → **子**コンポーネント → **孫**コンポーネント（上から下へステート値を渡す）

では、`src/user-data.json` のユーザー情報を、親（`App`）コンポーネントから孫（`User`）コンポーネントまでステート値を渡していき、ユーザー情報を表示します。  

2 . **孫**コンポーネント → **子**コンポーネント → **親**コンポーネント（下から上へステート値を渡す）

では、孫（`User`）コンポーネントの削除ボタンをクリックすることで、表示するステート値のユーザー情報を減らしていきます。

これらの流れを見ながら **ステートの値の行ったり来たり** を確認したいと思います。


## 5.1.上から下にステート値を渡す

では、まずはステートの値を上（親）から下（孫）に渡す流れを見ていきます。

今回使用するソースコードは下記のような構成となっています。

- 親コンポーネント: `src/App.tsx`
- 子コンポーネント: `src/UserList.tsx`
- 孫コンポーネント: `src/User.tsx`



### 5.1.1.親コンポーネントから子コンポーネントにステート値を渡す

まず、親（`App`）コンポーネントを見ていきます。

```tsx:src/App.tsx（親コンポーネント）
import { useState } from "react";
import userData from "./user-data.json";
import { UserList } from "./UserList";
import type { User } from "./user";

export default function App() {
  const [users] = useState<User[]>(userData);

  return (
    <div>
      <UserList users={users} />
    </div>
  );
}
```

`App` コンポーネントの構成は
1. ステート管理を `useState` で行う
2. ステート管理している値（`users`）を `UserList` コンポーネントに渡す
3. `UserLIst` を表示する

となっています。
 
では細かく見ていきましょう。

#### 1. ステート管理を `useState` で行う

ステート値を `useState` フックを用いて管理します。  

（ ↓ `useState` の参考記事）

https://qiita.com/daishiman/items/298750c188544980b863

下記の部分になります。

```tsx:
const [users] = useState<User[]>(userData);
```

まだ値を更新しないので、更新用の関数は記述していません。

`src/user-data.json` の `userData` を初期値として 変数 `users` に代入しています。


#### 2. ステート管理している値（`users`）を子コンポーネントに渡す

子（`UserList`）コンポーネントの `users` 属性に変数 `users` の値を渡しています。

```tsx:
<UserList users={users} />
```

上記の `UserList` コンポーネントの定義は、次の子（`UserList`）コンポーネントで行っています。


#### 3. `UserLIst` を表示する

`src/App.tsx` では、`UserList` コンポーネントを表示しています。


ここまでが、親（`App`）コンポーネントのステート値を渡す流れです。




### 5.1.2.子コンポーネントから孫コンポーネントにステート値を渡す

次に、親（`App`）コンポーネントから受け取ったステート値を孫（`User`）コンポーネントに渡す **子（`UserList`）コンポーネント** について見ていきます。  

今回は、`UserList` コンポーネントを使用しているのは `App.tsx` ですが、
コンポーネント（部品）という言葉の通り、いろいろなところで `UserList` コンポーネントを使用することができます。

これが、コンポーネントが便利な所以ですね。

話を戻して、ソースコードを見てきましょう。

```tsx:src/UserList.tsx（子コンポーネント）
import { FC } from "react";
import { User } from "./User";
import type { UserList as UserListType } from "./userList";

export const UserList: FC<UserListType> = ({
  users = [],
}) => {
  if (!users.length) return <div>表示できるユーザーはいません</div>;

  return (
    <div>
      {users.map((user) => (
        <User key={user.id} {...user} />
      ))}
    </div>
  );
};
```

`UserList` コンポーネントのは下記の構成でできています。

1. `UserList` はアロー関数で記述（[アロー関数の参考記事](https://qiita.com/daishiman/items/e497640caf68100454b9#53%E3%82%A2%E3%83%AD%E3%83%BC%E9%96%A2%E6%95%B0%E3%81%A7%E9%96%A2%E6%95%B0%E3%82%92%E4%BD%9C%E3%82%8B)）
2. 引数の `users = []` は `users: users = []` を省略
3. `users.length` が false （0）のときの返り値
4. `users.length` が false （0）でないときの返り値


#### 1. `UserList` はアロー関数で記述

最初はややこしく見えるかもしれませんが、噛み砕いて見てみるとアロー関数を使っています。


```tsx:
const const UserList: FC<UserListType> = () => {}
```

`UserLIst`コンポーネントは、これ ↑ を使っています。

#### 2. 引数の `users = []` は `users: users = []` を省略

`UserList` のアロー関数の引数の `users` というのは、  `users: users` を省略しています。（[参考記事](https://qiita.com/zukkis/items/0508f47e0ee72102f8ba)）  
なので、 `users` 属性に `users` の値を代入していることになります。

そして、`users` の値が **空** のときは `[]` がデフォルトで代入されます。

つまりこれらをまとめると、

`users = []` は `users: users = []` と同じで省略記法を用いて記述しています。


#### 3. `users.length` が false （0）のときの返り値

```tsx:
if (!users.length) return <div>表示できるユーザーはいません</div>;
```

の通り、`users.length` が false（0） のときは、 

```tsx:
<div>表示できるユーザーはいません</div>;
```

を表示します。

#### 4. `users.length` が false （0）でないときの返り値

`users.length` が false（0） でないときは、

```tsx:
    <div>
      {users.map((user) => (
        <User key={user.id} {...user} />
      ))}
    </div>
```

の処理が動きます。

`users` の配列を `map` 関数により `User` コンポーネントの配列に変換して表示します。

`{...user}` は、`user.id` 以外の `user.name`、`user.age` を一つにまとめて 孫（`User`）コンポーネントに渡しています。

### 5.1.3.孫コンポーネントでステート値を受け取る

最後に、孫（`User`）コンポーネントを見ていきます。

親（`App`）から子（`UserList`）そして孫（`User`）にステート値が渡ってきて、  
最後に渡ってきた値をどのように表示するかのコンポーネントとなっています。

```tsx:src/User.tsx（孫コンポーネント）
import { FC } from "react";
import type { User as UserType } from "./user";

export const User: FC<UserType> = ({ 
  name,
  age,
}) => {
  return (
    <section>
      <p>
        {name}（年齢：{age}歳）
      </p>
    </section>
  );
};
```


`User` コンポーネントの構成は
1. `UserList` はアロー関数で記述（[アロー関数の参考記事](https://qiita.com/daishiman/items/e497640caf68100454b9#53%E3%82%A2%E3%83%AD%E3%83%BC%E9%96%A2%E6%95%B0%E3%81%A7%E9%96%A2%E6%95%B0%E3%82%92%E4%BD%9C%E3%82%8B)）
2. 引数の `name` や `age` は `name: name` や `age: age` を省略
3. 名前と年齢を表示

となっています。

#### 1. `UserList` はアロー関数で記述

先ほどと同じ内容なので詳細は省略しますが、

```tsx:
const User: FC<UserType> = () => {};
```

ということですね。

#### 2. 引数の `name` や `age` は `name: name` や `age: age` を省略

こちらも同じなので、説明は省略しますが、

- `name` は `name: name` の省略系
- `yera` は `yera: age` の省略系


#### 3. 名前と年齢を表示

親コンポーネントから順番に渡ってきたステート値は、ここでやっと表示する形になります。

```tsx
  return (
    <section>
      <p>
        {name}（年齢：{age}歳）
      </p>
    </section>
  );
```

長い道のりでしたが、  
親（`App`）コンポーネントから孫（`User`）コンポーネントでまでステートの値を渡し、表示する流れをつかめたかと思います。

次は、孫（`User`）コンポーネントに削除バタンを定義して、削除するステート値を親（`App`）コンポーネントまで渡す流れを見ていきます。

## 5.2.下から上にステート値を渡す

今回の実装する概要は、下記の通りです。

- 孫コンポーネントに削除ボタンを定義
- 削除したステート値を親コンポーネントまで伝える
- 親コンポーネントでステートを更新
- 以降は、上からステート値を渡すと同じ流れ

では早速、孫コンポーネントに追記してきます。


### 5.2.1.孫コンポーネントに削除機能を追加

孫（`User`）コンポーネントに削除機能を追加します。

ここでやることは、  
クリックしてどのユーザーのイベントが発生したか（削除するか）を **子（`UserList`）コンポーネント** に伝えることです。  

```diff_tsx:src/User.tsx（孫コンポーネント）
import { FC } from "react";
import type { User as UserType } from "./user";

export const User: FC<UserType> = ({ 
+ id,
  name,
  age,
+ onRemove = (f) => f
}) => {
  return (
    <section>
      <p>
        {name}（年齢：{age}歳）
      </p>
+     <button onClick={() => onRemove(id)}>削除</button>
    </section>
  );
};
```

流れを見てみましょう。  

1. 削除ボタンをクリックイベントが発生する
2. 削除するための `onRemove` 関数を呼び出す
3. `onRemove` 関数は、 `User` コンポーネントに新たに追加されたプロパティ
4. `onRemove` 関数は、引数に `id` 値を受け取る（どのユーザーかを認識する）
5. `onRemove` 関数は、`id` を受け取り、`id` を返す
6. 孫（`User`）コンポーネントを使う子（`UserList`）コンポーネントに `id` を返す

このようにして、孫（`User`）コンポーネントから子（`UserList`）コンポーネントにステート値を渡します。


### 5.2.2.子コンポーネントに削除機能を追加

次に、孫（`User`）コンポーネントから伝わった `id` を親（`App`）コンポーネントに伝えるロジックを追加します。

孫（`User`）コンポーネントでやった `id` の受け渡しをここでもやります。

```diff_tsx:src/UserList.tsx（子コンポーネント）
import { FC } from "react";
import { User } from "./User";
import type { UserList as UserListType } from "./userList";

export const UserList: FC<UserListType> = ({
  users = [],
+ onRemoveUser = (f) => f
}) => {
  if (!users.length) return <div>表示できるユーザーはいません</div>;

  return (
    <div>
      {users.map((user) => (
-       <User key={user.id} {...user} />
+       <User key={user.id} {...user} onRemove={onRemoveUser} />
      ))}
    </div>
  );
};
```

では、流れを見ていきましょう。

1. 孫（`User`）コンポーネントから `onRemove` 属性の値として `id` を受け取る
2. 1.で受け取った `id` は、`onRemoveUser` 関数に代入される
3. `onRemoveUser` 関数は、 `UserList` コンポーネントに新たに追加されたプロパティ
4. `onRemoveUser` 関数に代入された `id` は、そのまま `id` を返す
5. 子（`UserList`）コンポーネントを使う親（`App`）コンポーネントに `id` を返す

流れは、孫（`User`）コンポーネントでやった内容とほぼ同じですね。


### 5.2.3.親コンポーネントに削除機能を追加

最後に、親（`App`）コンポーネントに削除機能を追加します。

ここでは、子（`UserList`）から親（`App`）に渡ってきたステート値を更新するためのロジックの追加が必要になります。



```diff_tsx:src/App.tsx（親コンポーネント）
import { useState } from "react";
import userData from "./user-data.json";
import { UserList } from "./UserList";
import type { User } from "./user";

export default function App() {
- const [users] = useState<User[]>(userData);
+ const [users, setUsers] = useState<User[]>(userData);

+ const removeUser = (id: string) => {
+   const newUsers = users.filter((user) => user.id !== id);
+   setUsers(newUsers);
+ };

  return (
    <div>
-     <UserList users={users} />
+     <UserList users={users} onRemoveUser={removeUser} />
    </div>
  );
}
```

では、最後の流れを見ていきましょう。

1. `useState` フックに `users` を更新する `setUser` 変数を追加する
2. 子（`UserList`）コンポーネントから `onRemoveUser` 属性の値として `id` を受け取る
3. 1.で受け取った `id` は、`removeUser` 関数に代入される
4. `removeUser` 関数は、  
  4.1. `users`　配列から `filter` 関数を用いて `user.id` が `id` と一致しないユーザーを抽出する  
  4.2. 4.1.で抽出したユーザーを `newUsers` 変数に代入する  
  4.3. ステート値を更新する `setUsers` に `newUsers` を渡す
5. `users` が更新され、上から下に再度ステート値が渡される
6. 以下「上から下にステート値を渡す」を繰り返す

となります。

最後の親（`App`）が少し複雑に感じられるかもしれませんが、このようにして `useState` フックを用いてステート値を管理します。

ただ、ステート値をバケツリレーのようにして渡していくので、コンポーネントの階層が多くなると複雑になっていきます。

これらを解消するフックやライブラリについては、また記事にしたいと思います。


# 6.おわりに
ステートの値を

- 親コンポーネント → 子コンポーネント → 孫コンポーネント（上から下へステート値を渡す）
- 孫コンポーネント → 子コンポーネント → 親コンポーネント（下から上へステート値を渡す）

と渡していく流れを見ることで、React 重要なステート管理を掴むことができました。

ただ、一つ一つステートを受け渡していくのは、めんどくさいなと感じました。

今回は、親から孫まででしたが、これがもっと階層が増えると管理しきれなくなりますね。

そのために、便利なフックやライブラリができたんだなと…

今後ステート管理のフックやライブラリについてもキャッチアップして React マスターに近づきたいなと思います。

最後まで読んでいただき、ありがとうございました😊

併せて他の記事も読んでいただけると嬉しいです🙇‍♂️

https://qiita.com/daishiman/items/298750c188544980b863

https://qiita.com/daishiman/items/547b73aa3779b81cc329

https://qiita.com/daishiman/items/e28a810323ae8b1b976f

https://qiita.com/daishiman/items/91d4ea25fe391217a00b

# 7.参考

- React ハンズオンラーニング web アプリケーション開発のベストプラクティス   
著: Alex Banks, Eve Porcello   
訳: 宮崎　空
- [【JavaScript】オブジェクトリテラルの省略記法](https://qiita.com/zukkis/items/0508f47e0ee72102f8ba)