react-query使ってみた

今開発中のアプリはfirebaseを軸に開発しているのだが、パフォーマンスが少々気になっていた。

パフォーマンスと一言で言っても、apiへのリクエスト数とデータが取得されるまでの時間といったデータアクセス部分に関して気になっていたという所。

そういえばキャッシュングしてないなと思っていたところ、少し前に同僚がreact-query良いよって言ってたのを思い出した。

そもそもなぜキャッシュ機構を入れていなかったのかというと、firestoreを使う場合には、デフォルトでキャッシュ機能が付いているからそれで対応すれば良いかーと考えていた。 後にfirestoreは有効期限が設定できないという点が判明して、今すぐ必要なわけじゃないから必要になった時になんとするかーと考えていた。

で、今に至り、結局firestoreを使っている箇所にキャッシュングは必要無くなったのだが、別の箇所で必要になった。

で、早速実装してみたのだが、すごく良い感じだ。

記事執筆時点では、基本的なuseQueryを使った実装を数箇所、既存のコードと入れ替える形でしか使っていないがもっと早く入れておくべきだったなと感じている。

現在利用している形としては、グローバルにrefetchを無くして有効期限だけを設定している。

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnMount: false,
      refetchOnWindowFocus: false,
      suspense: true,
    }
  }
})

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Todos />
    </QueryClientProvider>
  )
}

上記でwindow focus時とmount時に再度フェッチが走るのを無効にしている。 特にonMount時を無効化しておかないと、ページを遷移して戻ってくるたびに同じデータを取得してしまうので、再フェッチさせる必要のないページでは必須だ。

必要になればその時にrefetchを走らせれば良いだけ。

また、suspenseを有効化しておくことで、react18から使えるSuspenseに対応させることができる。

react-queryの使い方はhooks内もしくはコンポーネント配下で行う。 現プロジェクトはcomponent -> hooks -> repositoriesと構成されているので、基本的にhooks内にカスタムhooksとしてuseQueryを定義する。 ただ、実際のデータアクセス機能に関してはrepositoryに分離しておくことで、仮にreact-queryを使わなくなった場合にもhooksの修正だけで対応できる。ということにした。

const useMyQuery = (userId) => {
  const [user, setUser] = useState()
  const query = useQuery([“myQuery”, userId], () => userRepository.fetchUser(userId),                   {
  onSuccess(result) {
    if(!result.data.user) {
      throw new Error(“what a day!!”)
    }
  }
  })

  useEffect(() => {
    if(!query.data) return;
    setUser(query.data.data)
  }, [query])

  return {query, user}
}


const MyComponent = () => {
  const {user} = useMyQuery()

  return(
    <div>{user.name}</div>
  )
}

const SomeOuter = () => {
  return(
    <Suspense fallback={<div>loading</div>}>
      <MyComponent>
    </Suspense>
  )
}