Created
February 16, 2022 16:03
-
-
Save edzis/e97d83ee4eb7bf016f5dff165244ebd5 to your computer and use it in GitHub Desktop.
Image component that uses Blob URLs acquired via React Query with createObjectURL and cleanup with revokeObjectURL.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { Query, useQuery } from 'react-query'; | |
const QUERY_KEY_BASE = 'BlobImage'; | |
const BLOB_URL_CACHE_TIME = 5000; | |
const DEBUG = process.env.NODE_ENV === 'development' && true; | |
type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>; | |
const useBlobImageUrl = (src: ImageProps['src']) => { | |
const { data: blobUrl } = useQuery( | |
[QUERY_KEY_BASE, src], | |
async () => { | |
if (!src) { | |
return; | |
} | |
const res = await global.fetch(src); | |
const blob = await res.blob(); | |
const url = URL.createObjectURL(blob); | |
if (DEBUG) { | |
console.log('BlobImage createObjectURL', url); | |
} | |
return url; | |
}, | |
{ staleTime: Infinity, cacheTime: BLOB_URL_CACHE_TIME }, | |
); | |
return blobUrl; | |
}; | |
export const maybeRevokeBlobImageUrl = (query: Query) => { | |
if ( | |
typeof query.state.data !== 'string' || | |
query.queryKey[0] !== QUERY_KEY_BASE | |
) { | |
return; | |
} | |
const url = query.state.data; | |
if (DEBUG) { | |
console.log('BlobImage revokeObjectURL', url); | |
} | |
URL.revokeObjectURL(url); | |
}; | |
const BlobImage: React.VFC<ImageProps> = ({ src, ...rest }) => { | |
const blobUrl = useBlobImageUrl(src); | |
return <img src={blobUrl} {...rest} />; | |
}; | |
export default BlobImage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { default as React } from 'react'; | |
import { render } from 'react-dom'; | |
import { QueryClient, QueryClientProvider } from 'react-query'; | |
import BlobImage, { maybeRevokeBlobImageUrl } from './BlobImage'; | |
const queryClient = new QueryClient(); | |
queryClient.getQueryCache().subscribe((event) => { | |
if (!event) { | |
return; | |
} | |
if (event.type === 'queryRemoved') { | |
maybeRevokeBlobImageUrl(event.query); | |
} | |
}); | |
const Main = () => { | |
return ( | |
<> | |
<QueryClientProvider client={queryClient}> | |
<BlobImage src="some/path/that/returns/a/blob" /> | |
</QueryClientProvider> | |
</> | |
); | |
}; | |
const root = document.getElementById('root'); | |
render(<Main />, root); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment