ReactでAjaxでのローディングステートのuseStateが更新されないとき

# はじめに

Reactの開発を行っている中でuseStateの値が更新できないという問題が発生しました
初心者またはJavaScriptを理解してないからこそ起きそうな問題でしたのでまとめます

色々ネットを調べましたが目当ての記事を見つけられなかったので誰かの助けになればと思います

# 問題


メールアドレスを未入力の状態でログインを押した際にログインが[ローディング](https://chakra-ui.com/docs/components/button/usage#button-loading-state)になってアラートが出たらローディングをオフにして通常のボタンに戻したかったです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/3bbcfdd3-ead5-c73b-0f00-a8a0676600d8.png)


しかしこのコードではボタンがローディングになりません

```js:app.js
import { Button, Input } from "@chakra-ui/react";
import { useCallback, useState } from "react";
import { ChakraProvider } from "@chakra-ui/react";
import axios from "axios";

export const Test = () => {
  const [email, setEmail] = useState("");
  const [loading, setLoading] = useState(false);

  const emailChange = (e: any) => setEmail(e.target.value);

  const login = useCallback(() => {
    setLoading(true);
    axios
      .get(`https://jsonplaceholder.typicode.com/users`)
      .then(async (res) => {
        if (res.data) {
          console.log(res.data);
          setLoading(false);
        } else {
          setLoading(false);
        }
      });
    setLoading(false);
  }, []);

  const onClick = () => {
    login();
  };

  return (
    <ChakraProvider>
      <Input placeholder="email" value={email} onChange={emailChange} />
      <Button isLoading={loading} onClick={onClick}>
        ログイン
      </Button>
    </ChakraProvider>
  );
};
```



# 解決方法

```js:app.js
import { Button, Input } from "@chakra-ui/react";
import { useCallback, useState } from "react";
import { ChakraProvider } from "@chakra-ui/react";
import axios from "axios";

export const Test = () => {
  const [email, setEmail] = useState("");
  const [loading, setLoading] = useState(false);

  const emailChange = (e: any) => setEmail(e.target.value);

  const login = useCallback(() => {
    setLoading(true);
    console.log("Axiosスタート");
    axios
      .get(`https://jsonplaceholder.typicode.com/users`)
      .then(async (res) => {
        if (res.data) {
          console.log(res.data);
          setLoading(false);
        } else {
          setLoading(false);
        }
      });
    // setLoading(false); # ここを修正
    console.log("Axios中");
  }, []);

  const onClick = () => {
    login();
    console.log("onClick終了"); 
  };

  return (
    <ChakraProvider>
      <Input placeholder="email" value={email} onChange={emailChange} />
      <Button isLoading={loading} onClick={onClick}>
        ログイン
      </Button>
    </ChakraProvider>
  );
};

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/bc11b5bf-7da6-87f7-00b4-3505e383102e.png)

ポイントは`async`でAjaxの処理は非同期に行われるという事でした

ボタンをクリックすると`Axiosスタート`が表示され、`axios`がはじまります。これは非同期なので裏で動きつつ、`Axios中`が表示され、`onClick終了`が表示されて、最後に非同期で行われていたaxiosの結果が表示されました


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810513/9694c293-23ea-df19-a2be-d813d82926fe.png)

Ajaxが終わったタイミングで`loading`が`false`になるためログインボタンがローディングから通常のボタンに変わるわけです。

間違ったコードの場合は非同期でやっていることをわすれて、`Axios中`のところで`setLoading(false)`をしています。これでは裏で非同期にAjaxが動いているのに`loading`が`false`になってしまいます

それが原因で`useState`が更新されていないように思えてしまったのでした

# おわりに

AxiosとuseStateをあまり意識せずに利用していたので思ったよりも理解に時間がかかりました
このパターンはおそらくAjax特有ですが、初心者がはまってしまうところな気がしました