Skip to content

Instantly share code, notes, and snippets.

@413n
Last active April 12, 2022 21:56
Show Gist options
  • Save 413n/3ff9680e32ac207a9d07f28e4b297275 to your computer and use it in GitHub Desktop.
Save 413n/3ff9680e32ac207a9d07f28e4b297275 to your computer and use it in GitHub Desktop.

Hooks for images

Hooks for saving the images signed URLs from Amplify with S3 in the IndexedDB and use it to avoid a request for each image fetch. This because Amplify and S3 even for public storages needs signed URL with the unauthenticated permission.

import { useCallback, useEffect, useState } from "react";
import { useImageStorage } from "./useImageStorage";
interface useImageReturn {
image: string;
isLoading: boolean;
asyncLoad: (key: string) => Promise<string>;
asyncRevalidate: (key: string) => Promise<string>;
}
export type UseImageType = "offer" | "news" | "avatar";
export const useImage = (
type: UseImageType,
keyLoadedOnMount: string,
): useImageReturn => {
const [imageURL, setImageURL] = useState<string>(null);
const [isLoading, setIsLoading] = useState<boolean>(
Boolean(keyLoadedOnMount),
);
const {
asyncGetLocalImage,
asyncSetLocalImage,
asyncDelLocalImage,
asyncGetRemoteImage,
} = useImageStorage(type);
const saveAndReturnURL = useCallback(
(url: string): string => {
setImageURL(url);
return url;
},
[setImageURL],
);
const asyncLoad = useCallback(
async (key: string) => {
const localImageURL = await asyncGetLocalImage(key);
if (localImageURL) return saveAndReturnURL(localImageURL);
const s3ImageURL = await asyncGetRemoteImage(key);
await asyncSetLocalImage(key, s3ImageURL);
return saveAndReturnURL(s3ImageURL);
},
[
asyncGetLocalImage,
asyncGetRemoteImage,
asyncSetLocalImage,
saveAndReturnURL,
],
);
const asyncRevalidate = useCallback(
async (key: string) => {
await asyncDelLocalImage(key);
const loadedURL = await asyncLoad(key);
return loadedURL;
},
[asyncDelLocalImage, asyncLoad],
);
useEffect(() => {
async function loadImageOnMount(key) {
setIsLoading(true);
await asyncLoad(key);
setIsLoading(false);
}
if (keyLoadedOnMount) {
loadImageOnMount(keyLoadedOnMount);
}
}, [keyLoadedOnMount, asyncLoad]);
return { image: imageURL, isLoading, asyncLoad, asyncRevalidate };
};
import { Storage } from "aws-amplify";
import { createStore, del, get, set } from "idb-keyval";
import { useCallback } from "react";
import { isServer } from "@/utils/is";
const imageStore = !isServer && createStore("my-db", "images");
export const useImageStorage = (type: string) => {
const getIdbKey = useCallback((key: string) => `${type}.${key}`, [type]);
const asyncGetLocalImage = useCallback(
async (key: string) => get<string>(getIdbKey(key), imageStore),
[getIdbKey],
);
const asyncSetLocalImage = useCallback(
async (key: string, value: string) => {
try {
await set(getIdbKey(key), value, imageStore);
} catch (e) {
throw new Error(
`useImageStorage cannot store the image on key ${getIdbKey(key)}`,
);
}
},
[getIdbKey],
);
const asyncDelLocalImage = useCallback(
async (key: string) => del(getIdbKey(key), imageStore),
[getIdbKey],
);
const asyncGetRemoteImage = useCallback(
async (key: string) =>
Storage.get(key, {
level: "public",
cacheControl: "no-cache",
}),
[],
);
return {
asyncGetLocalImage,
asyncSetLocalImage,
asyncDelLocalImage,
asyncGetRemoteImage,
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment