質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

2261閲覧

カスタムフック内の処理が異常な回数行われる

maskmelon

総合スコア63

Next.js

Next.jsは、Reactを用いたサーバサイドレンダリングなどを行う軽量なフレームワークです。Zeit社が開発しており、nextコマンドでプロジェクトを作成することにより、開発環境整備が整った環境が即時に作成できます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2021/05/14 11:35

編集2021/05/14 12:44

ローカルストレージからトークンを取得してaxiosクライアントのリクエストヘッダに含める処理をカスタムフックで行なっているのですが、リクエストヘッダの設定処理が1回のページアクセスで200回から300回ほど行われてしまいます。

useAxiosはAPIからデータを取得する別のカスタムフックであるuseQuotesで呼び出されています。トップレベルでuseQuotesが呼び出されているページにアクセスすると上記の問題が生じます。

各hooksの実行回数を調べてみたのですが、useAxiosuseQuotesも数回しか呼び出されていないにも関わらず、トークン設定処理だけが過剰な回数行われているようです。

原因が思い浮かばず質問させていただきました。分かる方いらっしゃいましたらアドバイスを頂けると助かります。

typescript

1// useAxios.ts 2import axios, { AxiosError, AxiosResponse } from "axios"; 3import { useAuthMethods } from "./useAuthMethods"; 4 5export const useAxios = () => { 6 const { signout } = useAuthMethods(); 7 8 console.log("useAxios"); // 数回 9 10 axios.defaults.baseURL = process.env.NEXT_PUBLIC_API_BASE_URL; 11 12 axios.interceptors.request.use((config) => { 13 const token = localStorage.getItem("token"); 14 console.log(token); // 200から300回 15 config.headers = Object.assign( 16 { Authorization: `Bearer ${token}` }, 17 config.headers 18 ); 19 return config; 20 }); 21 22 const onSuccess = (response: AxiosResponse<any>) => response; 23 const onError = (error: AxiosError) => { 24 switch (error.response?.status) { 25 case 401: 26 signout(); 27 return; 28 } 29 }; 30 axios.interceptors.response.use(onSuccess, onError); 31 32 return { customAxios: axios }; 33}; 34

typescript

1// useQuotes.ts 2import { useContext, useState } from "react"; 3import { QuotesContext } from "../context/QuotesContext"; 4import { useAxios } from "./useAxios"; 5import { useEffectAsync } from "./useEffectAsync"; 6 7export const useQuotes = (tags: string[]) => { 8 console.log("useQuotes"); 9 const { quotes, setQuotes } = useContext(QuotesContext); 10 const [loading, setLoading] = useState<boolean>(false); 11 const { customAxios } = useAxios(); 12 13 useEffectAsync(async () => { 14 setLoading(true); 15 const url = "..." 16 const response = await customAxios.get(url); 17 if (response?.data) { 18 setQuotes(response.data); 19 } 20 setLoading(false); 21 }, [tags]); 22 23 return { quotes, loading }; 24}; 25

typescript

1// index.tsx 2interface Props { 3 registeredTags: string[]; 4} 5 6const Index: NextPage<Props> = ({ registeredTags }) => { 7 const [addedTags, setAddedTags] = useState<string[]>([]); 8 const { quotes, loading } = useQuotes(addedTags); 9 const { user, loading: userLoading } = useAuth(); 10 11 console.log("index"); 12 13 useEffect(() => { 14 if (!user && !userLoading) { 15 router.push("/login"); 16 } 17 return; 18 }, [user, userLoading]); 19 20 (略) 21 22 return ( 23 <> 24 25 (略) 26 27 </> 28 ); 29}; 30 31 32export default Index; 33

typescript

1// useEffectAsync.ts 2import { useEffect, DependencyList } from "react"; 3 4export const useEffectAsync = (effect: () => any, deps?: DependencyList) => { 5 useEffect(() => { 6 effect(); 7 }, deps); 8}; 9

イメージ説明

結果

typescript

1 const instance = axios.create({ 2 baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, 3 }); 4 5 instance.interceptors.request.use((config) => { 6 const token = localStorage.getItem("token"); 7 config.headers = Object.assign( 8 { Authorization: `Bearer ${token}` }, 9 config.headers 10 ); 11 return config; 12 });

上のようにaxiosインスタンスに関数を登録するように変更したところ、異常な関数実行はなくなりました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/05/14 11:38

Indexコンポーネント内に「const { quotes, loading } = useQuotes(addedTags);」とありますが、addedTagsはどこから来ているものでしょうか?
maskmelon

2021/05/14 11:42

useStateです。一部省略したときに消してしまったようです。申し訳ありません。 タグを指定して取得するデータを絞ることができるようにしているのですが、初期値の空配列の状態でも問題は発生します。
maisumakun

2021/05/14 11:45

useEffectAsyncはどのようなコードでしょうか?
maskmelon

2021/05/14 11:48

追加させていただきました。引数に非同期関数を入れるとuseEffect内で実行するようになっています。
maisumakun

2021/05/14 11:53

useEffectAsyncに仕掛けた関数内でlogを出してみたら、どの程度出るでしょうか? (useEffectAsync自体が何度も実行されているのか、それともaxios側だけ増えているのか見分けられるかと思います)
maskmelon

2021/05/14 12:12

一回のページアクセスで表示されるログの画像を載せました。useQuotes, useAxios, useEffectAsyncは同じタイミングで実行されているようです。 データフェッチの終了後にトークンのログのカウントが異常なスピードで増えて、その後もう一度レンダリングの上書き?でストップするような感じです。
guest

回答1

0

ベストアンサー

useAxiosが呼ばれるたびに、グローバルなaxiosへ何度もaxios.interceptors.request.useが行われて、結果1回のリクエストに対してトークンの付与処理が何度も何度も行われることとなってしまった、ということではないでしょうか。

投稿2021/05/14 12:16

編集2021/05/14 12:25
maisumakun

総合スコア145184

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/05/14 12:23

グローバルなaxiosを書き換えるのではなく、axios.create()で作った新たなインスタンスをカスタマイズして、それをstateあるいはrefに格納して使う、という流れが適当かと思います。 https://github.com/axios/axios#creating-an-instance
maskmelon

2021/05/14 12:27

`axios.interceptors.request.use`は関数の登録処理なので、各所で`useAxios`を呼ぶ度に蓄積されて、リクエスト時にまとめて実行されてしまう、ということでしょうか。そう考えると確かに納得です。 `axios`のクライアントをグローバルステートで一つだけ保持してそこにアクセスすれば解決できそうです。試してみます。
maskmelon

2021/05/14 12:29

参考情報もありがとうございます。的確な回答感謝です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問