Recoilでエラ〜メッセージを状態管理しよう！

## 前回の続き

https://qiita.com/uenomoto/items/e394dc44136e100fdadb

### Recoilについて若干分かったので言葉にしてまとめます
まずはRecoilをインストールします！！
```
npm install recoil
```

Docker composeを使用している方は
```
docker compose exec frontのサービス名 bash 
```
これでコンテナ内に入ってコンテナ内でインストールするか
この場合は、コンテナ内にはいったままです。
docker-compose.ymlにtty: trueオプション忘れずに！
コンテナが対話モードで起動することを保証します！

それか↓
```
docker compose run --rm frontのサービス名 npm install recoil
```

一時的なコンテナを作成し、Recoilをインストールし、その後でコンテナを削除することもできます！

どちらでもOKです！

インストールを確認できたら
あとは、_app.tsxファイルにあるJSXにRecoilRootで囲うだけ!
例↓
```jsx:_app.tsx
return(
	<RecoilRoot>
	  <Component {...pageProps} />
	</RecoilRoot>
)
```

### 次に今回はエラーメッセージをどこでも使えるように状態管理します
下準備として。
```typescript:recoil/atoms/errorMessageState.ts
import { atom } from "recoil";

export const errorMessageState = atom<string[] | string | null>({
  key: "errorMessageState",
  default: null,
});
```
keyがあるおかげで、同じerrorMessageStateでもそれぞれ違う一意に識別することができます。
keyで一意に識別し、デバック時に追跡し、必要に応じて永続化することもできるらしいです。
defaultで、初期値をnullにしています。普段からはrailsからのErrorMessageはないからです。

型定義として、配列の文字列と単一文字列とnullを許可しています。

### では先に更新関数を先に設定します
```typescript:state達
import { useRecoilValue, useSetRecoilState } from "recoil";
import { tokenState } from "@/recoil/atoms/tokenState";
import { errorMessageState } from "@/recoil/atoms/errorMessageState";

const token = useRecoilValue(tokenState);
const setErrorMessage = useSetRecoilState(errorMessageState);
```
使いたいコンポーネントの中身にRecoilを呼びます(import)
tokenStateはその名の通りtokenの状態管理をしています。
この場合、tokenは読み取りを行っています。
逆にsetErrorMessageは更新をしています。

例えば、仕入れ先を登録していて名前が空欄だった場合、仕入れ先登録のhandleSubmitの中身↓
```typescript
try {
	const res: AxiosResponse<Supplier> = await axios.post(
	  `${process.env.NEXT_PUBLIC_IP_ENDPOINT}/suppliers`,
	  params,
	  { headers: { Authorization: `Bearer ${token}` } } // ここでtokenを受け取り
	);
	if (res.status === 201) {
	  console.log("登録しました！");
	  setName("");
	  setContactInfo("");
	}
	setErrorMessage(null);
} catch (error: AxiosError | any) {
	console.log(error);
	setErrorMessage(error.response.data.errors); // railsから返されたエラーメッセージをステートに格納
}
```
成功時のsetErrorMessage(null);はerrorがないのでnullでお願いします!

そして今回の主役のエラー時に表示してほしい

`setErrorMessage(error.response.data.errors);`

これは実際に見ていただくとわかりやすいと思います。

console.log(error);で取得した部分です。
![スクリーンショット 2023-07-28 12.11.28（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124781/273b63dc-8ccb-dd27-f9e1-3d4cf560095c.png)

errorから始まり、response→data→errorsの中身が配列で`Name can’t be blank`とエラーが出ています。
エラー情報を取得させたいのはここだけなので、`setErrorMessage(error.response.data.errors);`になります。
今は、Name can’t be blankを`setErrorMessage`に格納している状態です。

### 次はビューに表示させたい
自分は、使い回しができるように、ErrorMessageコンポーネントを作成しました。

```jsx:ErrorMessage.tsx
import { XCircleIcon } from "@heroicons/react/20/solid";
import { errorMessageState } from "@/recoil/atoms/errorMessageState";
import { useRecoilValue, useSetRecoilState } from "recoil";

export const ErrorMessage = () => {
  const errorMessage = useRecoilValue(errorMessageState);
  const setErrorMessage = useSetRecoilState(errorMessageState);

  return (
    errorMessage && (
      <div className="rounded-md bg-red-50 p-4">
        <div className="flex">
          <XCircleIcon
            onClick={() => setErrorMessage(null)}
            className="h-10 w-10 cursor-pointer text-red-400"
            aria-hidden="true"
          />
          <div className="ml-3">
            <h3 className="text-md font-bold text-red-800">
              条件に満たさないため登録できませんでした
            </h3>
            <div className="mt-2 text-xl text-red-700">
              <ul role="list" className="text-left list-disc space-y-1 pl-5">
                {Array.isArray(errorMessage) ? (
                  errorMessage.map((error, index) => (
                    <li key={index}>{error}</li>
                  ))
                ) : (
                  <li>{errorMessage}</li>
                )}
              </ul>
            </div>
          </div>
        </div>
      </div>
    )
  );
};
```

`const errorMessage = useRecoilValue(errorMessageState);`
errorMessageは読み取り専用です

ここで、エラーがあった場合、すなわち先ほどのcatchの中身のsetErrorMessage(error.response.data.errors);で格納された情報を表示するようにしています。

最初の`errorMessage && (jsxのコード)`これは`setErrorMessage`の中身がnullでない時
すなわち、エラーがあったときのみ、エラーメッセージを表示するという意味です

`&&`は何何かつという意味だけでなくtrueの時だけ発動させたい時にも使えます！

次にliで箇条書きしています。今回の自分の場合、配列の時もあれば、単一の時もあるので、
三項演算子で条件分岐しています。
```jsx
{Array.isArray(errorMessage) ? (
  errorMessage.map((error, index) => (
    <li key={index}>{error}</li>
  ))
) : (
  <li>{errorMessage}</li>
)}
```

簡単に説明しますと、errorMessageは配列で格納されていますか？
されていたらmap関数で繰り返し処理をし表示させています

indexはkeyに設定し一意を約束します。でないと怒られますReactさんに

そして、配列でなく、単一なら、そのまま表示しています。
(因みにそのままの場合は、railsからではなく自分でなんか追加したい時に使えるかもと思ったからです。)

### 次にconst setErrorMessage = useSetRecoilState(errorMessageState);
今回の更新関数はエラーメッセージをnullに更新する時に使用する`setErrorMessage`です

```jsx
<XCircleIcon
  onClick={() => setErrorMessage(null)}
  className="h-10 w-10 cursor-pointer text-red-400"
  aria-hidden="true"
/>
```

XCircleIconはXボタンみたいな見た目をしているアイコンです。
それをクリックしたら、`setErrorMessage`をnullをして非表示にすることができます！
ずっとあると邪魔ですからね。。。

### そしてトップレベルのコンポーネントにErrorMessageをインポートします！
```jsx
const SupplierIngredientNew: NextPage = () => {
  return (
    <>
      <h1 className="text-xl lg:text-3xl">
        こちらのタブをクリックし仕入れ先か原材料の登録を初めてください
      </h1>
      <ErrorMessage />
      <SupplierRegistrationTab />
      <SupplierIngredientTable />
    </>
  );
};

export default SupplierIngredientNew;
```
ErrorMessageコンポーネントをレイアウトコンポーネントにおいてもいいのかもと思う今日この頃です。

### 見た目
![スクリーンショット 2023-07-28 12.11.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124781/cb4f70de-18a9-92e5-c55c-566834ec15a0.png)

クソデカバツボタンで消せますよアピールしています。
エラーメッセージの部分は現在は左寄りになってます。(紹介したコードは左寄りでした)
それと英語なので日本語にしないとダメですね。。。。これはRails側で設定した方がいい気がします。

**この感じでサクセスメッセージも作りまっす！**

### 最後に

以上で個人的学習になった部分まとめです！
戦いながら(PF作成しながら)成長していきたいです。
railsには少しは自信ありますが、React(Next.js)は初めてのPF作成です！
UdemyとyoutubeでShin Code先生の動画見た程度です！！

https://www.udemy.com/course/notion-nextjs-blog-dev/

https://www.youtube.com/watch?v=XQtkP8it9Ag

次はRails側で、Form objectを使ったデータの受け取り方とトランザクションを使った保存方法
(postmanでJSON確認済み)

Next側で画像を選択したら、AWSS3に投稿画像(セキュリティ面も配慮)をアップロードに成功したので

次はそれをNext側のフォームでRailsに送り保存でき表示でき次第
アウトプットできたらなと思います！！！

以上で終わります！お疲れ様でした！


### まとめ

https://qiita.com/uenomoto/items/7e846cc7b65ac8d96e9b

https://qiita.com/uenomoto/items/e394dc44136e100fdadb