useLoader

The useLoader hook allows you to fetch data from a Loader function which can be written alongside your components.

Loader functions are run on the server and have access to secure resources such as your database. Their code is never exposed to the browser.

components/Profile.js
import { useLoader } from 'firebolt' export function Profile({ id }) { const loader = useLoader(getUser, id) const user = loader.read() return ( <div> <div>Name: {user.name}</div> </div> ) } export async function getUser(ctx, id) { return await ctx.db('users').where({ id }).first() }

The component above loads data for a user using the getUser function and then displays their name.

useLoader(loaderFn, ...args)

The first argument must be a reference to your Loader function.

Additional arguments are passed to the loader function. These arguments are also used to construct a cache key for the data that the function returns and allows you to invalidate or update data in the cache manually.

The useLoader hook returns a loader instance specific to the data being loaded. Loaders are de-duplicated so if your app tries to fetch the same thing in multiple parts of your app it will only be fetched once.

Loader Function

The loader function must be exported from the same file or alternatively imported from somewhere else.

The function is provided with a Context object as its first argument. The Context object has methods to access cookies, redirects, and set data expiration. You can also decorate this object with access to a database or anything else in your firebolt.config.js file.

Additional arguments from the useLoader function are passed as additional arguments to your loader function.

loader.read()

Reads data from the loader. Firebolt seamlessly handles this during both server side streaming and on the client.

When loader.read() is called, if there is no data in the cache it will throw a promise to trigger Suspense boundaries allowing you to show loading UI until the data is fetched.

Once the promise resolves the component will re-render and loader.read() returns the data, finally rendering the component.

If the loader function fails, the promise will be rejected and the error will be thrown to the nearest ErrorBoundary allowing you to show a custom error UI.

loader.invalidate()

Calling invalidate() will mark loader data in the cache as stale.

If any components are observing this data it will be re-fetched in the background and automatically updated without triggering Suspense boundaries.

If no components are observing the data then the data is evicted, causing the next read to trigger Suspense boundaries as if the data was never there to begin with.

Note: data can also become invalidated if you mark it to expire after a certain amount of time. See the Context object that is passed into your loader function.

loader.set(newData)

This method allows you to manually update the data stored in the cache and re-render any components observing that data.

loader.edit(editFn)

This method, for convenience, allows you to make a partial update to your data and automatically handles immutability behind the scenes.

loader.edit(user => { user.name = 'Bob' })