React カスタムフック

# カスタムフック
カスタムフックとは、関数名が`use`で始まる独自のフックで、他のフックを呼び出せるJavaScriptの関数です。
UIと切り離してロジックごとに処理を抽出することで、カスタムフックの使い回しができるようになり、重複したロジックを取り除くことで保守性も向上します。
フックを呼び出すのはReact関数コンポーネント内のトップレベルのみであり、このルールを守ることでReactがフックの状態を保持し、コンポーネント内のすべてのstateを利用するロジックがソースコードから正しく参照される。

# カスタムフックを使用したサンプル
カスタムフックを用いて表示と処理の部分を分けたプログラムを作成しました。

https://codesandbox.io/embed/kasutamuhutuku-arenzi-nih6i8?fontsize=14&hidenavigation=1&theme=dark

## コード解説
### index.js
```javascript:index.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import App from "./components/App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);
```

### hooks.js
```javascript:hooks.js
import { useState } from "react";

// useHooksフックを定義、初期値を引数として渡す
export const useHooks = (initialValue) => {

  // ラジオボタン、セレクトボックス、テキストの現在の状態変数value
  // valueを更新する関数setValueを定義
  const [value, setValue] = useState(initialValue);

  // チェックボックスの現在の状態変数checkedValues
  // checkedValuesを更新する関数setCheckedValuesを定義
  // 初期値は空の変数[]を設定
  const [checkedValues, setCheckedValues] = useState(initialValue);

  // 選択（ラジオボタン、セレクトボックス）、入力された値を設定する関数handleChangeを定義
  const handleChange = (e) => {
    setValue(e.target.value);
  };

  // 選択（チェックボックス）された値を設定する関数checkedChangeを定義
  // 現在チェックされている値を配列として保持する
  const checkedChange = (e) => {
    if (checkedValues.includes(e.target.value)) {

      // チェックが外された場合、保持している配列から値を取り除く
      setCheckedValues(
        checkedValues.filter((checkedValue) => checkedValue !== e.target.value)
      );
    } else {

      // チェックされた場合、保持している配列に新たにチェックされた値を加える
      setCheckedValues([...checkedValues, e.target.value]);
    }
  };

  // useHooks関数の戻り値を設定
  return { value, checkedValues, handleChange, checkedChange };
};
```

### App.js
```javascript:App.js
// Checkコンポーネントをインポート
import { Check } from "./Check";

// Radioコンポーネントをインポート
import { Radio } from "./Radio";

// Selectコンポーネントをインポート
import { Select } from "./Select";

// Textコンポーネントをインポート
import { Text } from "./Text";

export default function App() {
  return (
    <>
      <Check />
      <hr />
      <Radio />
      <hr />
      <Select />
      <hr />
      <Text />
    </>
  );
}
```

### Items.js
```javascript:Items.js
// チェックボックス、ラジオボタン、セレクトボックスで使用する要素を定義
// 他のファイルで使用できるようエクスポートしておく
export const items = [
  { id: 1, item: "HTML" },
  { id: 2, item: "CSS" },
  { id: 3, item: "JavaScript" },
  { id: 4, item: "PHP" }
];
```

### Check.js
```javascript:Check.js
// Titleコンポーネントをインポート
import { Title } from "./Title";

// useHooksカスタムフックをインポート
import { useHooks } from "../hooks";

// チェックリストで使用する要素itemsをインポート
import { items } from "./Items";

// Checkコンポーネント（親コンポーネント）からpropsを受けとる
const CheckList = (props) =>

  // map()を用いてチェックボックスの要素を１つひとつ設定する
  items.map((item) => {
    return (
      <label key={item.id}>
        <input
          type="checkbox"
          value={item.item}

          // チェックされる、またはチェックが外されると関数checkedChangeが実行される
          onChange={props.checkedChange}
        />
        {item.item}
      </label>
    );
  });

export const Check = () => {

  // useHooksフックを利用する
  // 初期値に空の配列[]を定義する
  // useHooksで設定した戻り値から必要な変数、関数を取得
  const { checkedValues, checkedChange } = useHooks([]);
  return (
    <div>
      <Title title="チェックボックス" />
      <p>選択された値: {checkedValues.join("、 ")}</p>
      <CheckList checkedChange={checkedChange} />
    </div>
  );
};
```

### Radio.js
```javascript:Radio.js
// Titleコンポーネントをインポート
import { Title } from "./Title";

// useHooksカスタムフックをインポート
import { useHooks } from "../hooks";

// チェックリストで使用する要素itemsをインポート
import { items } from "./Items";

// Radioコンポーネント（親コンポーネント）からpropsを受けとる
const RadioList = (props) =>

  // map()を用いてラジオボックスの要素を１つひとつ設定する
  items.map((item) => {
    return (
      <label key={item.id}>
        <input
          type="radio"
          value={item.item}
          name="radio"

          // チェックされる、またはチェックが外されると関数checkedChangeが実行される
          onChange={props.handleChange}
        />
        {item.item}
      </label>
    );
  });

export const Radio = () => {

  // useHooksフックを利用する
  // 初期値に空白文字""を定義する
  // useHooksで設定した戻り値から必要な変数、関数を取得
  const { value, handleChange } = useHooks("");

  return (
    <div>
      <Title title="ラジオボタン" />
      <p>選択された値: {value}</p>
      <RadioList handleChange={handleChange} />
    </div>
  );
};
```

### Select.js
```javascript:Select.js
// Titleコンポーネントをインポート
import { Title } from "./Title";

// useHooksカスタムフックをインポート
import { useHooks } from "../hooks";

// styles.cssをインポート
import "../styles.css";

// チェックリストで使用する要素itemsをインポート
import { items } from "./Items";

// Selectコンポーネント（親コンポーネント）からpropsを受けとる
const SelectList = (props) =>

  // map()を用いてセレクトボックスの要素を１つひとつ設定する
  items.map((item) => {
    return (
      <option value={item.item} key={item.id}>
        {item.item}
      </option>
    );
  });

export const Select = () => {

  // useHooksフックを利用する
  // 初期値に空白文字""を定義する
  // useHooksで設定した戻り値から必要な変数、関数を取得
  const { value, handleChange } = useHooks("");

  return (
    <div>
      <Title title="セレクトボックス" />
      <p>選択された値: {value}</p>
      <select onChange={handleChange}>
        <option></option>
        <SelectList />
      </select>
    </div>
  );
};
```

### Text.js
```javascript:Text.js
// Titleコンポーネントをインポート
import { Title } from "./Title";

// useHooksカスタムフックをインポート
import { useHooks } from "../hooks";

export const Text = () => {

  // useHooksフックを利用する
  // 初期値に空白文字""を定義する
  // useHooksで設定した戻り値から必要な変数、関数を取得
  const { value, handleChange } = useHooks("");

  return (
    <div>
      <Title title="入力フォーム" />
      <p>入力された値: {value}</p>
      <input type="text" onChange={handleChange} />
    </div>
  );
};
```

### Title.js
```javascript:Title.js

// 他のファイルでもタイトルを使用できるようエクスポートする
export const Title = (props) => {
  return <p>{props.title}</p>;
};
```