Images and other Media
After setting up Reactive Data Client for structured data fetching, you might want to incorporate some media fetches as well to take advantage of suspense and concurrent mode support.
Storing ArrayBuffer
Resource and Entity should not be used in this case, since they both represent string -> value map structures. Instead, we'll define our own simple Endpoint.
import { Endpoint } from '@data-client/react';
export const getPhoto = new Endpoint(async ({ userId }: { userId: string }) => {
const response = await fetch(`/users/${userId}/photo`);
const photoArrayBuffer = await response.arrayBuffer();
return photoArrayBuffer;
});
- useSuspense
- useCache
- JS/Node
// photo is typed as ArrayBuffer
const photo = useSuspense(getPhoto, { userId });
// photo will be undefined if the fetch hasn't completed
// photo will be ArrayBuffer if the fetch has completed
const photo = useCache(getPhoto, { userId });
// photo is typed as ArrayBuffer
const photo = await getPhoto({ userId });
Just Images
In many cases, it would be useful to suspend loading of expensive items like images using suspense. This becomes especially powerful with the fetch as you render pattern in concurrent mode.
@data-client/img provides use with <Img />
component that suspends, as well as getImage
endpoint to prefetch.
Installation
- NPM
- Yarn
- pnpm
- esm.sh
yarn add @data-client/img
npm install --save @data-client/img
pnpm add @data-client/img
<script type="module">
import * from 'https://esm.sh/@data-client/img';
</script>
Usage
import React, { ImgHTMLAttributes } from 'react';
import { useSuspense } from '@data-client/react';
import { Img } from '@data-client/img';
export default function Profile({ username }: { username: string }) {
const user = useSuspense(UserResource.get, { username });
return (
<div>
<Img
src={user.img}
alt="React Logo"
style={{ height: '32px', width: '32px' }}
/>
<h2>{user.fullName}</h2>
</div>
);
}
Prefetching
Note this will cascade the requests, waiting for user to resolve before the image request can start. If the image url is deterministic based on the same parameters, we can start that request at the same time as the user request:
import React, { ImgHTMLAttributes } from 'react';
import { useSuspense, useFetch } from '@data-client/react';
import { Img, getImage } from '@data-client/img';
export default function Profile({ username }: { username: string }) {
const imageSrc = `/profile_images/${username}}`;
useFetch(getImage, { src: imageSrc });
const user = useSuspense(UserResource.get, { username });
return (
<div>
<Img
src={imageSrc}
alt="React Logo"
style={{ height: '32px', width: '32px' }}
/>
<h2>{user.fullName}</h2>
</div>
);
}
When using the fetch as you render pattern in concurrent mode, Controller.fetch() with the getImage
Endpoint to preload the image.