Kris
Kris

Reputation: 31

How to use RTK Query data for derived state?

I am building a small app that tracks progress of mountain summit peak lists for hikers. I am using RTK Query for the first time for the majority of state management. I need to create a piece of derived state, a "list counts" object, which contains key value pairs of peak list IDs and the number of peaks completed on that list. Data returned from two RTK Query hook calls is needed to calculate it and this derived data is needed in multiple components:

const listCounts = {
  nh4k: 6,
  ne100: 5,
  usHigh: 3,
  // more data
}

I have two query hooks from RTK Query which work to fetch the necessary data from the backend. My current working solution is a custom hook which calls each hook generated by RTK Query and returns listCounts:

const useListCounts = () => {
  const { data: peakLists } = useGetPeakListsQuery();
  const { data: logEntries } = useGetLogEntriesQuery();
  // logic to create listCounts object
  return listCounts;
}

Is this appropriate use of a custom hook? I don't have a ton of experience using custom hooks but most other examples I have seen return some value from a built in React hook such as useState, useReducer etc.

Another solution I have considered is to create a separate function, call each RTK query hook in the component directly and then pass the data to the function in the component:

// in util function file:

const calculateListCounts = (peakLists, logEntries) => {
  // logic to create listCounts object
  return listCounts;
}

// In component file:

import { calculateListCounts } from "..."

const MyComponent = () => {
  const { data: peakLists } = useGetPeakListsQuery();
  const { data: logEntries } = useGetLogEntriesQuery();
  const listCounts = calculateListCounts(peakLists, logEntries);
  // return JSX
}

Is there any reason to prefer either option above, or is there some way to access RTK query cache outside of a React component or custom hook so that I can calculate and return listCounts with a single function call?

I have several other similar situations where some piece of derived state calculated using data returned from RTK Query hooks is needed across multiple components and want to make sure I'm following the correct pattern.

Upvotes: 3

Views: 760

Answers (1)

faloi
faloi

Reputation: 401

I'm no expert either, but I believe using a custom hook is better than a plain function. The advantages I see are:

  • you encapsulate everything there, so clients don't need to care about how the list is computed,
  • you can combine and propagate the isLoading, isFetching and any other metadata of the queries you need,
  • if, in a future, you decide that computation is too expensive for the client and move it to your server, you only need to change that custom hook and not every component that uses that data.

The only suggestion I have is to respect the query interface as much as possible, so your components can use it as any other query from your server:

const useListCountsQuery = () => {
  const { data: peakLists, isLoading: peakLoading } = useGetPeakListsQuery();
  const { data: logEntries, isLoading: logLoading } = useGetLogEntriesQuery();
  
  // We use useMemo hook to avoid recalculating the derived state except the original data changes 
  const listCounts = useMemo(
    () => // your logic,
    [peakLists, logEntries]
  )

  // We mimic RTK query interface so we can use this derived data as any other query
  return { data: listCounts, isLoading: peakLoading || logLoading }
}

Upvotes: 0

Related Questions