Hero img
React-query

React-queryを触ってPOST GET処理が楽になりました。

React-queryで通信回数の抑制、画面レンダリングの抑制が簡単にできました。fetchで更新があった場合のみ画面の再レンダリングする。無駄にサーバに問い合わせさせない、画面をレンダリングさせないようにすることができます。


目次

  • React Query
  • なにが良かったか?
  • 環境条件
  • React Queryの基本
  • React Query使う方法
  • 必要な作業
  • QueryClient
  • QueryClientProvider
  • useQuery
  • useQueryオプション
  • 定期的に取得する
  • キャッシュの時間
  • 取得後レンダー制御
  • データの取得「成功、失敗時」
  • fetch無効化
  • queryデータを取得
  • デバック
  • デバックテスト
  • つまづきそうなところ
  • QueryClientProvider
  • キャッシュ
  • ページ移行でキャッシュを使ってくれない
  • 引数を渡して実行
  • まとめ

React Query

React Queryはstate管理ライブラリーらしい。
どういう場面で使用するかと言うとサーバに問い合わせるHTTP POST、GETの回数を減らしたい、キャッシュを使用して欲しい時に使用するものです。取得データはページ移行しても維持しているため無駄にサーバーに問い合わせをすることが無くなります。
サーバに問い合わせする必要がある場合はこのライブラリーは必須だと感じていましたが、React 18でsuspenseが使えるようになったので使わなくてもいい状況も出てきました。

なにが良かったか?

個人的に良かったところ

  • 定期的にGetしてくれる機能がある
  • キャッシュを利用してるのでサーバーアクセス回数が減る
  • 子コンポーネントに受け渡しができる。
  • 取得データを比較して同じであればレンダーしない。
    POST、GET処理は上記全ての機能が必要なのではないでしょうか? あと欲を言えばデータをローカルストレージに保存してそれを使用する処理。2022-09-16今現在ベータ版だそうですがpersistQueryClient があります。

環境条件

  • react18.2.0
  • npx create-react-app my-appを使用する。
  • post getテスト用サーバーはnode js expressとmysqlを使用しましたが...
  • mockableのサービスを使う HTTP GET POSTのテストは登録不要のmockableを使えば簡単です。

React Queryの基本

公式ページ

まずはインストール

npm i @tanstack/react-query

react-queryには現在 react-queryと@tanstack/react-queryがあり。今は@tanstack/react-queryに統合されたよう。 また、@tanstackは「useQuery(['uid'])」と配列でkeyを指定する必要がある。

とりあえずソースコード

src/App.js
import logo from './logo.svg';
import './App.css';
import {
  useQuery,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query'

const queryClient = new QueryClient()

function App() {
  return (
    <div className="App">
      <QueryClientProvider client={queryClient}>
        <Component1/>
      </QueryClientProvider>
    </div>
  );
}
export default App;


const Component1 = (()=>{
  const query = useQuery(['uid'], Getcfetch)
  if(query.isLoading)return(<div>isLoding.....</div>)
  return (<div>
    <p>aaaaaaaaa</p>
    <p>{query.data.msg}</p>
  </div>)
})

//fetchgeturlは各自用意する必要があります。
const Getcfetch = (async()=>{
  const fetchgeturl = "http://demo8905806.mockable.io/decs";
  const products = await fetch(fetchgeturl, {
      method: "GET",
  })
      .then(datas => datas.json())
      console.log(products);
  return products
})

GETでHellow worldが表示されれば成功です。 output-fetch.png

React Query使う方法

React Queryの使うには以下の3つを用意する必要がある

必要な作業

  1. 1.クライアントを作成
  2. 2.プロバイダーを用意
  3. 3.queryを使用する
    QueryClientとQueryClientProviderは使用する親に宣言するため、基本はindex.jsもしくはApp.jsに記述する事が多いと思います。

QueryClient

クライアントを作成します。一番上の親に一度宣言するだけで大丈夫です。

QueryClientProvider

QueryClientProviderにクライアントを指定し、囲みます。 プロバイダーが無くてもできるのでは?と色々見て試してみましたがどうやら必要らしいです。

useQuery

useQueryにユニークキーとfetchファンクション + optionsを与えることで。 ファンクションが実行されその戻り値が保持されます。

useQueryオプション

公式ページでかなり多くのオプションが用意されていますが、以下9個のオプションだけでほとんど状況に対応できると思います。

const SuccessEvent = ((data) =>{
  console.log(data);
});
const ErrorEvent = ((data) =>{
  console.log(data);
})
const SettledEvent = ((data) =>{
  console.log(data);
})

  const navigate = useNavigate();
  const query = useQuery(['uid'], Getcfetch , {
      refetchInterval: 100000,
      cacheTime: 600000,//60 * 10000 * 10,
      staleTime: Infinity,
      notifyOnChangeProps: ["data"],  
      onSuccess : (data) => SuccessEvent(data) ,
      onError : (data) => ErrorEvent(data) , 
      onSettled : (data) => SettledEvent(data) , 
      refetchOnWindowFocus : false , 
      enabled : true
  })

定期的に取得する

refetchIntervalを利用すると指定した間隔で再度ファンクションを実行します。 1000 = 1秒になっています。

キャッシュの時間

cacheTimeとstaleTimeを利用します。

  • cacheTime:データを維持する時間。
  • staleTime:最新の情報だと判断する時間。
    cacheが残っていれば利用し、その後最新の情報を取得します。
    が、staleTimeの有効時間内であればキャッシュデータが最新の情報と判断し、サーバーに対して問い合わせを行いません。
    そのためキャッシュが残っていてもstaleTimeが無ければ、サーバに問い合わせを行います。何度も問い合わせをさせたくないのでstaleTimeはInfinityと設定しています。

取得後レンダー制御

fetchで値を取得しましたが、デフォルトの設定では取得ごとにfetchの時間等、取得データ以外も変わるため画面レンダリングが実行されてしまいます。取得できた情報が異なる場合のみ画面に反映させたいのでnotifyOnChangeProps: ["data"]を設定します。 つまり取得「"data"」が異なった場合のみと指定しています。 この設定をすることによってfetch後の取得データが同じだった場合レンダリングが行われません。

データの取得「成功、失敗時」

fetch「成功時,失敗時、最後に」の3つの状態でファンクションを実行することができます。 try catch finallyと同じでね。

fetch無効化

enabled : trueとかなり簡単で普通のオプションですが、 上記の「データの取得成功時」のみファンクションを実行するような使い方で、特定のユーザー、ログイン状態、特定の処理後など、条件によってfetchを実行させるかどうか柔軟に対応できます。

queryデータを取得

親からデータを取り出したい場合はuseQueryを使用すると普通にデータの中身を取得できます。 あまり使う事はないとは思いますが...

//親で宣言したところに
const queryClient = new QueryClient()
  <QueryClientProvider client={queryClient}>
        //...省略
    const data = queryClient.getQueryData()

デバック

デバック用の確認画面も用意されています。 query-dev.jpg 公式ページ 導入もとても簡単です。

npm i @tanstack/react-query-devtools

後はインポートをしてプロバイダー内に記入してするだけです。

import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

//...省略
        <ReactQueryDevtools initialIsOpen={false} />
      </QueryClientProvider>

react-router-domを使う場合でも特に問題ありません。

src/App.js
import logo from './logo.svg';
import './App.css';
import {
  useQuery,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { BrowserRouter , Routes, Route,useNavigate } from "react-router-dom";
function App() {
const queryClient = new QueryClient()
  const getcatch = (()=>{
    const data = queryClient.getQueryData()
   console.log(data);
  })
  return (
    <div className="App">
      <QueryClientProvider client={queryClient}>
      <button onClick={getcatch}>getdata</button>
        <BrowserRouter>
        <Routes>
          <Route path="/" element={<Component1/>}/>
          <Route path="/b" element={<Component2/>}/>
          </Routes>
        </BrowserRouter>
        <ReactQueryDevtools initialIsOpen={false} />
      </QueryClientProvider>
    </div>
  );
}
export default App;


const Component1 = (()=>{
  const navigate = useNavigate();
  const query = useQuery(['uid'], Getcfetch , {
      refetchInterval: 100000,
      cacheTime: 600000,//60 * 10000 * 10,
      staleTime: Infinity,
      notifyOnChangeProps: ["data"],  
  })
  if(query.isLoading)return(<div>isLoding.....</div>)
  return (<div>
    <p>aaaaaaaaa</p>
    <button onClick={(()=>(navigate('/b')))}>routeB</button>
    <p>{query.data.msg}</p>
  </div>)
})


const Component2 = (()=>{
  const navigate = useNavigate();
  const query = useQuery(['uuid'], Getcfetch ,{
      refetchInterval: 100000,
      cacheTime: 600000,//60 * 10000 * 10,
      staleTime: Infinity,
      notifyOnChangeProps: ["data"],
  } )
  if(query.isLoading)return(<div>isLoding.....</div>)
  return (<div>
    <p>bbbbbbbbbb</p>
    <button onClick={(()=>(navigate('/')))}>routeHome</button>
    <p>{query.data.msg}</p>
  </div>)
})
const Getcfetch = (async()=>{
  const fetchgeturl = "http://demo8905806.mockable.io/decs";
  const products = await fetch(fetchgeturl, {
      method: "GET",
  })
      .then(datas => datas.json())
      console.log(products);
  return products
})

デバックテスト

上記のコードで「useNavigate」を使ってページ移行してもfechをしないことが確認できました。 また、キャッシュが存在しない場合。古い場合そしてインターバル時間によってはfechを実行しています。 query-dev.jpg

つまづきそうなところ

QueryClientProvider

プロバイダーっで必要ですか?と思いましたが必ず必要でした。 react-router-domを使用している場合ページ移行は必ずuseNavigateを使う必要があります。

キャッシュ

キャッシュが存在する場合でもfetchが走ってしまいサーバに問い合わせをしてしまいます。 そのためstaleTimeが存在しています。
キャッシュが存在していて、尚且つstaleTimeの時間内(最新のデータ)だと判断された場合は何もせずキャッシュのデータを使用します。

ページ移行でキャッシュを使ってくれない

ページ移行にはaタグを使用せずにnavigateを使用しましょう。

引数を渡して実行

post時にデータを渡す事があると思います。useQueryにそのまま引数を渡すと処理はしてくれるのですが、取得データが無いと言われてしまいます。その場合は即時関数を使用してください。

//  const { data, isLoading } = useQuery(['Event'],getsql(props))<-- undefined
  const { data, isLoading } = useQuery(['Event'], () => getsql(props))

まとめ

正直まだまだ使いこなせていません。next.jsの場合はクライアントをstateにすると正常動作したり、いまだに謎です。
react-querは確かにGET POST時に使用すると,とても便利です。必須だとも感じます。デバックモードもかなり完成度が高くて助かっています。さらにreduxまたはrecoilを使用してより分かりやすくstateを使用しなければならないのですが...どのようにするのが正解なのか...

関連記事

コメント

コメントを書く

質問や、間違いがありましたら、お気軽にどうぞ

※お名前とコメントの内容を入力してください。
※全てこのブログ上に公表されます。
※許可なく管理者によって修正・削除する場合がございます。 詳しくはプライバシーポリシーを参照ください