React-Queryとは?使い方と重要性を分かりやすく解説

React-Queryライブラリ

React-Queryを開始する前の前提条件ございます。

React フックと関数コンポーネントに関する中級レベルのスキルが不可欠です。

JavaScriptでのREST APIとデータ取得の基本的な理解。

マシンにNode.jsがインストールされていることを確認してください。

React-Queryとは?

React-Queryは、ReactおよびNext.jsコードベース用に構築されたライブラリであり、ネットワークリクエストを作成する際に従うべきすべてのベストプラクティスが組み込まれています。

ReactおよびNext.jsアプリでのサーバー状態の取得、キャッシュ、同期、および更新を簡単にする React用のデータ取得ライブラリという事です。

これは、キャッシュをフェッチしてサーバーからデータを同期する方法を簡素化するReact ライブラリであり、Reactの不足しているデータフェッチライブラリとしてよく説明されますが、より技術的な用語で言えば、React アプリケーションで上記のすべてのプロセスを簡単に実行できます。

React自体には、サーバーからデータをフェッチする方法に関する意見はありません。

最も基本的なアプローチは、コンポーネントが最初に useEffectにマウントされたときにブラウザのフェッチAPIを使用し、次にuseStateを使用して応答を管理することです。

このプロセスは機能しますが、キャッシュ、再試行、重複排除などの要件がある場合です。

データ取得コードを簡素化するだけでなく、これらの複雑な要件をすぐに処理できます。

つまり、React-Queryを使用すると、グローバルな状態を変更することなく、シンプルかつ宣言的な方法でReactベースのアプリケーションのデータをフェッチ、キャッシュ、および更新できます。

React-Queryは、Reactのエコシステムで最も人気のあるライブラリの1つになりつつあります。

ReduxやuseEffectとの違いは?

React-Queryは「Axios」や「fetch API」と同じではなく、「Redux」または「Context API」の代替品でもないことを明確にする必要があります。

React-Queryは主にデータ同期に使用され、「Redux」はグローバルな状態管理に使用されます。

useEffectなどの一般的なデータ取得パターンとの違いは、React-Queryは前回取得したデータを返し、その後もう一度取得し直すという点です。

もしリソースが最初と同じであれば、React-Queryはページの再読み込みを強制することなく、両方のデータを参照として保持致します。

React-Queryを使用する理由

これはデータを取得するだけなのか?私たち開発者は、JS FetchAPIまたはAxiosuseEffectおよびuseStateフックを使用してそれらを行ってきました。

しかし、突然useEffectと useStateの代わりに React-Queryを使用するのはなぜなのか?

ご存じかもしれませんが、ほとんどの従来の管理ライブラリはクライアント状態の操作には優れていますが、非同期またはサーバー状態の操作では煩わしくなりました。

React-Queryを使用する主な理由の1つは、頻繁な更新、キャッシュ、およびサーバーとの同期が必要な非同期データを扱う場合、React-Queryが非常に便利であることです。

ただし、React-Queryがこのような機能を提供するためには、その概念や使い方について理解する必要があります。

そのため、React-Queryを使用する前に、そのドキュメントやチュートリアルをしっかりと学習し、適切に使いこなすことが重要です。

前述した通り、React-Queryは、グローバルな状態がなくてもデータの取得、キャッシュ、更新を簡素化できるため、使いやすく、必要に応じてカスタマイズできます。

これにより、従来の方法よりも効率的に非同期データを処理できるようになります。

React-Queryを使用したデータ取得

CRAで新しいReact JSプロジェクトを作成し、react-queryをインストールします。

npm i react-query
// or
yarn add react-query

次に、App.jsファイルでreact-queryからuseQueryをインポートします。

// App.js

import { useQuery } from "react-query";

単純なfetchData関数を記述してRest APIからデータをフェッチします。

本日はfetch()を使用していきますが、Axiosやその他の方法でも可能です。

JSONプレースホルダAPI にネットワークリクエストを行うasync関数を定義する必要があります。

// App.js

const fetchData = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  return res.json();
};

そしてuseQueryフックを使用して、下記のようにデータ取得を管理できます。

const response = useQuery("users", fetchData);

useQueryフックには2つの引数が必要となります。

1つはこのクエリのkeyです。

そのために、文字列のusersを使用しています。

最初の引数として配列を置くこともでき、配列が渡された場合では各アイテムは安定したクエリキーにシリアル化されます。

2つ目は、データをフェッチする関数となります。

先述で作成したfetchDataのasync関数を渡します。

また、さまざまなオプションの3番目の引数としてオブジェクトを渡すことが可能です。

useQueryから返される応答は非常に重要となっています。

下記のプロパティがあります。

data

error

failureCount

isError

isFetchedAfterMount

isFetching

isIdle

isLoading

isPreviousData

isStale

isSuccess

refetch

remove

status

dataは取得した実際のデータです。

statusは、応答に応じてloading、error、success、またはidleになります。

上記これらすべてのプロパティにはさまざまな用途があります。

本日は、datastatuserrorおよびisErrorプロパティを使用する例を示します。

const { data, status } = useQuery("users", fetchData);

前述したように、useQueryフックは2つの引数を取り、最初の引数はキャッシュする文字列であり、クエリ結果を追跡します。

2番目の引数は、HTTPリクエストを作成するために定義するfetchData関数です。

React-Queryは、キャッシュとデータの更新を内部的に処理します。

これで、データを使用してブラウザに表示する事ができます。

// App.js

import { useQuery } from "react-query";

const fetchData = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  return res.json();
};
const App = () => {
  const { data, status } = useQuery("users", fetchData);
  return (
    <div className="App">
      {status === "error" && <p>Error fetching data</p>}
      {status === "loading" && <p>Fetching data...</p>}
      {status === "success" && (
        <div>
          {data.map((user) => (
            <p key={user.id}>{user.name}</p>
          ))}
        </div>
      )}
    </div>
  );
};

export default App;

上記で行った事は、statusを確認してデータを表示することです。

これは、React-QueryのuseQueryフックの使用方法を簡単に説明したものとなります。

QueryClientProvider

基本的には、React.jsアプリケーションでReact-Queryを構成するには、データ取得を必要とする最上位のコンポーネントQueryClientProviderコンポーネントでラップする必要があります。

QueryClientは、他のコンポーネントがキャッシュを利用できるようにします。

React-Queryを起動するために、ルートディレクトリのファイルindex.jsに下記のような基本的な設定をします。

// index.js v18〜

import React from 'react';
import { createRoot } from 'react-dom/client';
import 'index.css';
import App from './App';

import { QueryClient, QueryClientProvider } from "react-query";

const root = ReactDOM.createRoot(document.getElementById('root'));

const queryClient = new QueryClient();

root.render(
<React.ScriptMode>
//ラップ
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.ScriptMode>
);
// index.js 

import React from 'react'
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient();
ReactDOM.render(
//ラップ
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>,
    document.getElementById('root')
);

QueryClientProviderの子コンポーネントは、React-Queryライブラリが提供するフックにアクセスできるようになり、QueryClientインスタンスが提供されるようになりました。

このインスタンスを使用し、React-Queryライブラリが提供するフックにアクセスすることになります。

クエリクライアントをインスタンス化し、コンポーネントツリーに提供すると、すべての子コンポーネントがプロのようにデータをフェッチできるようになります。

これは開発中のデバッグに使用される事も多いです。

import {useQuery} from 'react-query';

const fetchData = async () => {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  return res.json();
};

function Users(){
    const {data, status} = useQuery('users', fetchData) 

    if(status === "Loading"){
        return <div>Loading...</div>
    }
    if(status === "isError"){
        return <div>Loading…</div>
    }

    return(
        <div className='container'>
        <h1>Users</h1>
        {
            data.map((user, index) => {
                return <li key={index}>{user.title}</li>
            })
        }

        </div>
    )
}

export default Users;

コンポーネントreturnキーワードの前に、基本的なロジックを実行し、アプリケーションがロード状態にあるかどうか、またはエラーが発生したかどうかを確認します。

useQueryから返される応答にerrorisErrorプロパティを使用するのが基本です。

 const {data, status, error, isError} = useQuery('users', fetchData) ;

    if(status === "Loading"){
        return <div>Loading...</div>
    }
    if(isError){
        return <div>Error! {error. message}</div>
    }

React-Queryを使用すると、エラー処理も簡単になります。

上記の通り、React-Queryはサーバーの状態を管理するための最良のライブラリの1つです。

アプリデータがユーザーを制御し始める前に制御することが可能となります。

クエリを並列で実行したい場合は、useQueryを並べて書けば問題ありません。

ただし、配列に基づいてクエリしたい場合や、Suspenseを使っている場合はuseQueriesを使う必要がありますので注意して下さい。

const userQueries = useQueries(
  users.map((user) => {
    return {
      queryKey: ['user', user.id],
      queryFn: () => fetchUserById(user.id),
    };
  }),
)``;

もう少し、詳しく学ばれたい方は下記へ進んで下さい。

クエリ

クエリするには下記の2つが必要となります。

ユニークな独自のKey

Promise(データをresolveするか、またはエラーを投げる)

const result = useQuery('users', fetchData);
// or
const result = useQuery({
  queryKey: 'users',
  queryFn: fetchData,
});

isLoading or status === 'loading'

初期ロード中

isError or status === 'error'

エラーが発生した状態

error

エラー内容

isSuccess or status === 'success'

データ取得が成功した状態

isIdle or status === 'idle'

クエリが無効された状態

isFetching or isLoading

なんらかの通信中である

クエリキー

クエリキーに基づいてキャッシュが行われる。

シリアライズ可能な値ならなんでもKeyとして使用できる。

useQuery('users', ...)
// queryKey === ['users']

useQuery(['user', 5], ...)
// queryKey === ['user', 5]

useQuery(['user', 5, { preview: true }], ...)
// queryKey === ['user', 5, { preview: true }]

クエリが特定のid等に基づいて行われるものであるならば、その値をキーとして含めておくことが重要です。

function Users({ userId }) {
  const result = useQuery(['users', userId], () => fetchDataById(userId));
}

クエリを実行する関数

データ取得に失敗した時は必ずエラーを投げることです。

axiosとは異なり、fetchはデフォルトではエラーを投げないので注意する必要があります。

import { useQuery } from 'react-query';

// React QueryライブラリのuseQueryフックを使用して、APIからユーザーデータを取得する関数を定義する
useQuery(['users', userId], async () => {
  // APIエンドポイントにアクセスし、レスポンスを取得するために、fetch関数を使用する
  const response = await fetch('/users/' + userId);
  // fetch関数はデフォルトでエラーを投げないため、レスポンスがエラーである場合は、エラーを投げる必要がある
  // レスポンスがエラーであるかどうかは、okプロパティを確認することで判断できる
  if (!response.ok) {
    // レスポンスがエラーである場合は、エラーを投げる
    throw new Error('ネットワークのレスポンスが良くありません。');
  }
  // レスポンスが正常である場合は、JSONデータを解析して返す
  return response.json();
});

上記のコード例は、React-QueryライブラリのuseQueryフックを使用して、APIからユーザーデータを取得する関数を定義しています。

関数内では、「fetch」関数を使用してAPIエンドポイントにアクセスし、レスポンスを取得します。

「fetch」関数はPromiseを返すため、レスポンスが帰ってくるまで待機する必要があります。

その後、レスポンスの「okプロパティ」を確認して、レスポンスがエラーでないことを確認します。

レスポンスがエラーの場合は、エラーを投げて関数を中断します。

レスポンスが正常な場合は、JSONデータを解析して返します。

エラーを投げることで、プログラムが問題を認識し、適切に対処できるようになります。

また、React-Queryライブラリによってキャッシュされたデータが返される可能性があるため、常にAPIからデータを取得するために、関数を渡す必要があります。

バックグラウドでのデータ取得をユーザーに通知

isFetchingではあらゆる通信でtrueとなります。

idLoadingは初回データ取得の時だけtrueとなります。

アプリ全体での通信状態を取得したい場合ではuseIsFetchingを使用して下さい。

以下のコードに、Reactアプリ内でデータを取得している場合に、取得中かどうかを示すフラグである「isFetching」と、初回データ取得時かどうかを示すフラグ「isLoading」を使用する方法を示します。

import React, { useState, useEffect } from "react";

function App() {
  // データを管理するstate
  const [data, setData] = useState([]);

  // データ取得中かどうかを管理するstate
  const [isFetching, setIsFetching] = useState(false);

  // 初回のデータ取得中かどうかを管理するstate
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      // データ取得中であることを示すフラグを設定する
      setIsFetching(true);

      // データを取得する
      const response = await fetch("https://example.com/data");
      const data = await response.json();
      
      // データを更新する
      setData(data);

      // データ取得が完了したことを示すフラグを設定する
      setIsFetching(false);
      setIsLoading(false);
    };

    // 初回のアプリのレンダリング時にデータを取得する
    fetchData();
  }, []);

  return (
    <div>
      {isLoading ? ( // 初回データ取得中であればLoading...を表示する
        <p>Loading...</p>
      ) : ( // データをUIに表示する
        <ul>
          {data.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

上記の例では、useEffectフックを使用して、初回のアプリのレンダリング時にデータを取得しています。

このとき、「isLoading」フラグをtrueに設定して、初回のデータ取得が行われたことを示します。

データの取得中には、「isFetching」フラグをtrueに設定し、データの取得が完了したら「false」に戻します。

これにより、UIが取得中であることをユーザーに通知することができます。

最後に、「isLoading」フラグをfalseに設定することで、初回のデータ取得が完了したことを示し、データをUIに表示することができます。

また、アプリ全体での通信状態を知りたい場合には、react-queryライブラリの「useIsFetching」フックを使用することで、現在アクティブなAPIリクエストの数を取得することができます。

以下のようにimportして使用することができます。

import { useIsFetching } from "react-query";

Mutations

データの作成・更新・削除する場合はuseQueryではなくuseMutationフックを使用します。

// useMutationフックを使用して、データを作成・更新・削除する方法
// `useMutation`フックは、axiosなどのHTTPクライアントを使用してAPIを呼び出すことができます。

// useMutationフックをインポートします
import { useMutation } from 'react-query';

// useMutationフックを使用して、API呼び出しを実行します
const { mutate, isLoading, isError, isSuccess, error, data } = useMutation(
  (newUser) => axios.post('/users', newUser) // データを作成する場合はPOSTリクエストを使用します
  // データを更新する場合はPUTリクエストを使用します
  // データを削除する場合はDELETEリクエストを使用します
);

// mutate関数を使用して、APIを呼び出します
mutate({ id: 1234, title: 'test' }); // ここで、実際のデータを渡します

上記のコードは、以下のことを行っています。

useMutationフックを使用して、API呼び出しを実行するための準備をします。

そしてmutate関数を使用して、APIを呼び出します、データを渡すことで、APIに必要な情報を提供します。

API呼び出しの結果を、「isLoading」、「isError」、「isSuccess」、「error」、「data」などの変数で受け取ります。

「isLoading」、「isError」、「isSuccess」、「error」、「data」の各変数は、以下のような役割を持っています。

isLoading: API呼び出しが進行中であるかどうかを示すブール値です。

isError: API呼び出し中にエラーが発生したかどうかを示すブール値です。

isSuccess: API呼び出しが成功したかどうかを示すブール値です。

error: API呼び出し中に発生したエラーに関する情報を含むオブジェクトです。

data: API呼び出しの結果として取得したデータを含むオブジェクトです。

クエリの無効化と停止

enabledオプションをfalse設定することで以下のようになります。

キャッシュデータが存在する場合は、isSuccess状態になり、データが提供されます。

キャッシュデータがない場合はisIdle状態になり、マウント時にはクエリが実行されません。

バックグラウンドでは再クエリされません。

invalidateQueriesおよびrefetchQueriesが発火された場合でも、再クエリしません。

refetchを使って手動でクエリを実行することは可能です。

enabledオプションは、クエリの実行結果を使って別のクエリを実行する際に使用します。

下記の例では、「userId」が存在する場合にのみこのクエリが実行されるように設定されています。

const { isIdle, data: projects } = useQuery(
  ['projects', userId],
  getProjectsByUser,
  {
   // userIDが存在する場合のみこのクエリを実行する
    enabled: !!userId,
  },
);

「enabled」オプションがfalseに設定されている場合、クエリは手動で再実行する必要があります。

invalidateQueriesおよびrefetchQueriesを使用しても、クエリは再実行されません。

また、「enabled」オプションをtrueに設定すると、クエリが自動的に再実行されます。

最後に

React-Queryは、Webアプリケーションでデータを管理するためのライブラリです。

例えば、APIからデータを取得する際に、データの取得状態を管理し、必要に応じて再取得することができます。

これまで、Reactアプリケーションでデータ管理を行うために「Redux」や「Context API」が使われてきましたが、React-Queryを使うことで、より簡単にデータ管理ができるようになりました。

React-Queryを使うことで、APIからデータを取得する処理をカスタムフックに分離することができます。

これにより、コンポーネント内でデータ取得に関するロジックを記述する必要がなくなり、コードの再利用性や保守性が向上します。

また、React-Queryは、データの取得状態やエラーの管理、キャッシュ機能などを備えているため、データの取得に関するロジックを自分で実装する必要がありません。

そのため、コードの量が削減され、開発効率が向上します。

React-Queryは、Reactアプリケーションでデータ管理を行うために、非常に便利なライブラリです。

初学者でも比較的簡単に学ぶことができるため、今後のReactアプリケーション開発において、活用することをお勧めします。

本日は以上となります。

最後まで読んで頂きありがとうございます。

プライバシーポリシー

© 2023 Icons8 LLC. All rights reserved.

© [deve.K], 2023. React logo is a trademark of Facebook, Inc. JavaScript is a trademark of Oracle Corporation and/or its affiliates. jQuery and the jQuery logo are trademarks of the JS Foundation. TypeScript and the TypeScript logo are trademarks of the Microsoft Corporation. Next.js and the Next.js logo are trademarks of Vercel, Inc. Firebase and the Firebase logo are trademarks of Google LLC. All logos edited by [deve.K].