Skip to content

Instantly share code, notes, and snippets.

@jeremy-code
Created March 6, 2024 02:58
Show Gist options
  • Save jeremy-code/42255192c2268dcbab83f8a37ed34258 to your computer and use it in GitHub Desktop.
Save jeremy-code/42255192c2268dcbab83f8a37ed34258 to your computer and use it in GitHub Desktop.
useProgressStore hook, get progress from a ky event handler
// when you create ky instance, set onDownloadProgress: progressStore.setSnapshot,
import { useSyncExternalStore } from "react";
import type { DownloadProgress } from "ky";
export const shallowCompare = <T extends Record<string, unknown>>(obj1: T, obj2: T) => {
if (Object.is(obj1, obj2)) return true;
if (!(obj1 instanceof Object) || !(obj2 instanceof Object)) return false;
const entries1 = Object.entries(obj1);
const entries2 = Object.entries(obj2);
return (
entries1.length === entries2.length && !entries1.some(([key, value]) => obj2[key] !== value)
);
};
const createProgressStore = () => {
const subscribers = new Set<() => void>();
let progress: DownloadProgress = { percent: 0, transferredBytes: 0, totalBytes: 0 };
const subscribe = (subscriber: () => void) => {
subscribers.add(subscriber);
return () => {
subscribers.delete(subscriber);
};
};
const getProgress = () => progress;
const setProgress = (newProgress: DownloadProgress) => {
// Since `useSyncExternalStore` will re-render the component if the snapshot changes through `Object.is`
// comparison, the object (and thus, reference) is only updated if any of the properties have changed.
if (!shallowCompare(progress, newProgress)) {
progress = newProgress;
}
subscribers.forEach((subscriber) => subscriber());
};
return { subscribe, getSnapshot: getProgress, setSnapshot: setProgress };
};
export const progressStore = createProgressStore();
export const useProgressStore = () =>
useSyncExternalStore(progressStore.subscribe, progressStore.getSnapshot);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment