From 0e3cec5ddfebe58f1cf17db261f570fda7ee79ea Mon Sep 17 00:00:00 2001 From: Liam Ross Date: Mon, 20 Apr 2020 12:23:20 -0700 Subject: [PATCH] feat(Image): can handle rejected or falsy src with onRenderFallback --- src/components/Image/Image.stories.tsx | 22 +++++++++++++- src/components/Image/Image.tsx | 41 ++++++++++++++++++-------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/components/Image/Image.stories.tsx b/src/components/Image/Image.stories.tsx index b368480..a6d7dc5 100644 --- a/src/components/Image/Image.stories.tsx +++ b/src/components/Image/Image.stories.tsx @@ -25,7 +25,7 @@ export const Basic = () => ( ); -export const WithPromiseSrc = () => ( +export const WithSrcPromise = () => (
( />
); + +export const SrcPromiseRejects = () => ( +
+ setTimeout(() => rej(), 500))} + onRenderLoading={() => } + onRenderFallback={() => 'Rejected source promise'} + /> +
+); + +export const SrcPromiseReturnsFalsy = () => ( +
+ setTimeout(() => res(''), 500))} + onRenderLoading={() => } + onRenderFallback={() => 'Falsy source'} + /> +
+); diff --git a/src/components/Image/Image.tsx b/src/components/Image/Image.tsx index 8abd998..feca1c7 100644 --- a/src/components/Image/Image.tsx +++ b/src/components/Image/Image.tsx @@ -8,33 +8,48 @@ export interface ImageProps extends Remove, * The image source can be a `Futurable` or `LazyFuturable`, or undefined. If * undefined or if a promise will display as loading. */ - src?: FuturableOrLazy; + src?: FuturableOrLazy; /** * Render out an element to be shown while src is loading. */ onRenderLoading?(): ReactNode; + /** + * Render out an element to be shown if the . + */ + onRenderFallback?(): ReactNode; } export const Image = forwardRef( - ({ src, onRenderLoading, alt, className, ...imgProps }, ref) => { - const [source, setSource] = useState(typeof src === 'string' ? src : undefined); + ({ src, onRenderLoading, onRenderFallback, alt, className, ...imgProps }, ref) => { + const sourceIsNotPromise = typeof src === 'string' || !src; + const [loading, setLoading] = useState(!sourceIsNotPromise); + const [source, setSource] = useState( + sourceIsNotPromise ? (src as string | undefined) : undefined, + ); - const getSource = useCallback(async (srcGetter: FuturableOrLazy) => { - const fetchedSource = await futureableOrLazyToFuturable(srcGetter); - setSource(fetchedSource); + const getSource = useCallback(async (srcGetter: FuturableOrLazy) => { + setLoading(true); + let fetchedSource = undefined; + try { + fetchedSource = await futureableOrLazyToFuturable(srcGetter); + } catch {} + setLoading(false); + setSource(fetchedSource || undefined); }, []); useEffect(() => { - if (typeof src === 'string' || src === undefined) return setSource(src); + if (sourceIsNotPromise) { + setLoading(false); + setSource((src as string | undefined) || undefined); + return; + } getSource(src); - }, [src, getSource]); + }, [getSource, sourceIsNotPromise, src]); const imageClass = classnames('ui__image', className); - return source ? ( - {alt} - ) : ( - <>{onRenderLoading?.()} - ); + if (loading) return <>{onRenderLoading?.()}; + if (!source) return <>{onRenderFallback?.()}; + return {alt}; }, );