|
| 1 | +--- |
| 2 | +title: Data Library Integration |
| 3 | +description: 'Since the release of v6.4 some folks wonder if React Router is attempting to replace libraries like React Query. The answer is "nope!".' |
| 4 | +--- |
| 5 | + |
| 6 | +# Data Library Integration |
| 7 | + |
| 8 | +Since the release of v6.4 some folks wonder if React Router is attempting to replace libraries like [React Query][react-query], [useSwr][useswr], etc. |
| 9 | + |
| 10 | +The answer is "nope!". |
| 11 | + |
| 12 | +React Router's data APIs are about _when_ to load, mutate, and revalidate data, but not _how_ to do it. It's about the data lifecycle, not the actual implementation of data fetching, mutation, storage, and caching. |
| 13 | + |
| 14 | +Considering that `<a href>` and `<form action>` are both navigation events, and both coupled to data (what data to show or what data to change), it makes sense that a client side router would help you with the _navigation state_ of both elements. But the actual data implementation is up to you. |
| 15 | + |
| 16 | +The examples here were adapted from [TkDodo's blog][tkdodo], thank you for the great post! |
| 17 | + |
| 18 | +## Loading Data |
| 19 | + |
| 20 | +Loaders can use your data abstraction inside of loaders. Note that this loading happens outside of the React render lifecycle, so you can't use hooks like React Query's `useQuery`, you'll need to use the query client's methods directly. |
| 21 | + |
| 22 | +```jsx |
| 23 | +import { queryClient } from "./query-client"; |
| 24 | + |
| 25 | +export const loader = ({ params }) => { |
| 26 | + return queryClient.fetchQuery(queryKey, queryFn, { |
| 27 | + staleTime: 10000, |
| 28 | + }); |
| 29 | +}; |
| 30 | +``` |
| 31 | + |
| 32 | +If the query client throws errors correctly, then React Router's [`errorElement`][errorelement] will work the same. |
| 33 | + |
| 34 | +Of course, you can use all of the features of the data library, like caching, so ensure when the user clicks the back button to a page you've already seen, the data is loaded from the cache immediately. |
| 35 | + |
| 36 | +React Router only retains the _current page's loaderData_. If users click "back", all loaders are called again. Without a data caching library like React Query (or HTTP cache headers on your JSON API to use the browser's own HTTP cache), your app will refetch all of the data again. |
| 37 | + |
| 38 | +In this way, React Router is about _timing_, where React Query is about _caching_. |
| 39 | + |
| 40 | +## Accessing Data in Components |
| 41 | + |
| 42 | +While React Router's `useLoaderData` returns whatever you returned from your loader, you can use your data abstraction's hooks instead to get access to the full feature set of that package. |
| 43 | + |
| 44 | +```diff |
| 45 | +export default function SomeRouteComponent() { |
| 46 | +- const data = useLoaderData(); |
| 47 | ++ const { data } = useQuery(someQueryKey); |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +## Invalidating Data in Mutations |
| 52 | + |
| 53 | +Because most of these library's have some mechanism for caching, you'll need to invalidate those caches at some point. |
| 54 | + |
| 55 | +The perfect place to invalidate those caches is in a React Router [action][action]. |
| 56 | + |
| 57 | +```jsx lines=[5] |
| 58 | +import { queryClient } from "./query-client"; |
| 59 | + |
| 60 | +export const action = async ({ request, params }) => { |
| 61 | + const formData = await request.formData(); |
| 62 | + const updates = Object.fromEntries(formData); |
| 63 | + await updateContact(params.contactId, updates); |
| 64 | + await queryClient.invalidateQueries(["contacts"]); |
| 65 | + return redirect(`/contacts/${params.contactId}`); |
| 66 | +}; |
| 67 | +``` |
| 68 | + |
| 69 | +## Conclusion |
| 70 | + |
| 71 | +With all of these APIs working together, you can now use [`useNavigation`][usenavigation] from React Router to build pending states, optimistic UI, and more. Use React Router for timing of data loading, mutations, and navigation state, then use libraries like React Query for the actual implementation of loading, invalidating, storage, and caching. |
| 72 | + |
| 73 | +[react-query]: https://tanstack.com/query/v4/ |
| 74 | +[useswr]: https://swr.vercel.app/ |
| 75 | +[errorelement]: ../route/error-element |
| 76 | +[action]: ../route/action |
| 77 | +[tkdodo]: https://tkdodo.eu/blog/react-query-meets-react-router |
| 78 | +[usenavigation]: ../hooks/use-navigation |
0 commit comments