Data fetching
One of the most powerful aspects of found is it's built in support
for efficient data fetching. Routes, can specify getData
functions
to fetch from API's or other sources.
Loading data on navigation
import "./styles.css"; import { createBrowserRouter, HttpError } from "found"; import PostsPage from "./PostsPage"; import PostPage from "./PostPage"; const API = "https://dummyjson.com"; const Router = createBrowserRouter({ routeConfig: [ { path: "posts", getData: async () => { const resp = await fetch(`${API}/posts`); if (!resp.ok) throw new HttpError(404); return resp.json(); }, Component: PostsPage, children: [ { path: "/:postId", getData: async ({ params }) => { const resp = await fetch(`${API}/posts/${params.postId}`); if (!resp.ok) throw new HttpError(404); return resp.json(); }, Component: PostPage, }, ], }, ], }); export default function App() { return <Router />; }
Above is a simple "List, detail" view of imaginary blog posts. Along the side is a list of
of posts and clicking on any navigates to the "detail" view of the post.
Even though these routes are nested, data is fetched in parallel. /posts
and /posts/1
are triggered
together and the UI waits for both to complete before rendering.
Avoiding unnecessary server trips
The example above isn't as efficient as it could be. When you switch routes both products and the product detail route are updated, which triggers fetching the list of posts again, even though we already have it and it probably hasn't changed.
For simple cases "memoizing" our getData
function based on the the input each function needs
works great.
import "./styles.css"; import { createBrowserRouter, HttpError } from "found"; import memoize from "memoize-one"; import PostsPage from "./PostsPage"; import PostPage from "./PostPage"; const API = "https://dummyjson.com"; const Router = createBrowserRouter({ routeConfig: [ { path: "posts", Component: PostsPage, getData: memoize( async () => { const resp = await fetch(`${API}/posts`); if (!resp.ok) throw new HttpError(404); return resp.json(); }, // Only fetch once, the first time () => true ), children: [ { path: "/:postId", Component: PostPage, getData: memoize( async ({ params }) => { const resp = await fetch( `${API}/posts/${params.postId}` ); if (!resp.ok) throw new HttpError(404); return resp.json(); }, // Only fetch again when `postId` changes ([{ params: lastParams }], [{ params }]) => lastParams.postId === params.postId ), }, ], }, ], }); export default function App() { return <Router />; }
Why doesn't Found memoize getData
calls automatically?
Because generalized caching is hard! Found can't always know what the right behavior is for your app, so instead of guessing we give you the tools to do it your way.
This example is over simplified and doesn't cover the richness in use-cases that data fetching
entails. You probably do want to refetch posts occasionally to ensure the data stays
fresh. Maybe you want to the data to loading as soon as new posts are available, or when the
user refocuses the page after being inactive. This where data fetching libraries
like react-query
, relay
, apollo
and others start to make sense.
These libraries all handle the hard work of syncing server data to browsers and keeping it fresh and up to date. Found is a great companion to these libraries! Found provides the hooks for efficient data loading and triggers by mapping your data needs to the URL.