Skip to content

Commit 8ac39c7

Browse files
committed
fix(use-image): sync with beta
1 parent d487902 commit 8ac39c7

File tree

1 file changed

+190
-51
lines changed

1 file changed

+190
-51
lines changed

packages/hooks/use-image/src/index.ts

+190-51
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,160 @@
1+
// /**
2+
// * Part of this code is taken from @chakra-ui/react package ❤️
3+
// */
4+
// import type {ImgHTMLAttributes, MutableRefObject, SyntheticEvent} from "react";
5+
6+
// import {useEffect, useRef, useState} from "react";
7+
// import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect";
8+
9+
// type NativeImageProps = ImgHTMLAttributes<HTMLImageElement>;
10+
11+
// export interface UseImageProps {
12+
// /**
13+
// * The image `src` attribute
14+
// */
15+
// src?: string;
16+
// /**
17+
// * The image `srcset` attribute
18+
// */
19+
// srcSet?: string;
20+
// /**
21+
// * The image `sizes` attribute
22+
// */
23+
// sizes?: string;
24+
// /**
25+
// * A callback for when the image `src` has been loaded
26+
// */
27+
// onLoad?: NativeImageProps["onLoad"];
28+
// /**
29+
// * A callback for when there was an error loading the image `src`
30+
// */
31+
// onError?: NativeImageProps["onError"];
32+
// /**
33+
// * If `true`, opt out of the `fallbackSrc` logic and use as `img`
34+
// */
35+
// ignoreFallback?: boolean;
36+
// /**
37+
// * The key used to set the crossOrigin on the HTMLImageElement into which the image will be loaded.
38+
// * This tells the browser to request cross-origin access when trying to download the image data.
39+
// */
40+
// crossOrigin?: NativeImageProps["crossOrigin"];
41+
// loading?: NativeImageProps["loading"];
42+
// }
43+
44+
// type Status = "loading" | "failed" | "pending" | "loaded";
45+
46+
// export type FallbackStrategy = "onError" | "beforeLoadOrError";
47+
48+
// type ImageEvent = SyntheticEvent<HTMLImageElement, Event>;
49+
50+
// /**
51+
// * React hook that loads an image in the browser,
52+
// * and lets us know the `status` so we can show image
53+
// * fallback if it is still `pending`
54+
// *
55+
// * @returns the status of the image loading progress
56+
// *
57+
// * @example
58+
// *
59+
// * ```jsx
60+
// * function App(){
61+
// * const status = useImage({ src: "image.png" })
62+
// * return status === "loaded" ? <img src="image.png" /> : <Placeholder />
63+
// * }
64+
// * ```
65+
// */
66+
// export function useImage(props: UseImageProps = {}) {
67+
// const {loading, src, srcSet, onLoad, onError, crossOrigin, sizes, ignoreFallback} = props;
68+
69+
// const imageRef = useRef<HTMLImageElement | null>();
70+
// const firstMount = useRef<boolean>(true);
71+
// const [status, setStatus] = useState<Status>(() => setImageAndGetInitialStatus(props, imageRef));
72+
73+
// useSafeLayoutEffect(() => {
74+
// if (firstMount.current) {
75+
// firstMount.current = false;
76+
77+
// return;
78+
// }
79+
80+
// setStatus(setImageAndGetInitialStatus(props, imageRef));
81+
82+
// return () => {
83+
// flush();
84+
// };
85+
// }, [src, crossOrigin, srcSet, sizes, loading]);
86+
87+
// useEffect(() => {
88+
// if (!imageRef.current) return;
89+
// imageRef.current.onload = (event) => {
90+
// flush();
91+
// setStatus("loaded");
92+
// onLoad?.(event as unknown as ImageEvent);
93+
// };
94+
// imageRef.current.onerror = (error) => {
95+
// flush();
96+
// setStatus("failed");
97+
// onError?.(error as any);
98+
// };
99+
// }, [imageRef.current]);
100+
101+
// const flush = () => {
102+
// if (imageRef.current) {
103+
// imageRef.current.onload = null;
104+
// imageRef.current.onerror = null;
105+
// imageRef.current = null;
106+
// }
107+
// };
108+
109+
// /**
110+
// * If user opts out of the fallback/placeholder
111+
// * logic, let's just return 'loaded'
112+
// */
113+
// return ignoreFallback ? "loaded" : status;
114+
// }
115+
116+
// function setImageAndGetInitialStatus(
117+
// props: UseImageProps,
118+
// imageRef: MutableRefObject<HTMLImageElement | null | undefined>,
119+
// ): Status {
120+
// const {loading, src, srcSet, crossOrigin, sizes, ignoreFallback} = props;
121+
122+
// if (!src) return "pending";
123+
// if (ignoreFallback) return "loaded";
124+
125+
// try {
126+
// const img = new Image();
127+
128+
// img.src = src;
129+
// if (crossOrigin) img.crossOrigin = crossOrigin;
130+
// if (srcSet) img.srcset = srcSet;
131+
// if (sizes) img.sizes = sizes;
132+
// if (loading) img.loading = loading;
133+
134+
// imageRef.current = img;
135+
// if (img.complete && img.naturalWidth) {
136+
// return "loaded";
137+
// }
138+
139+
// return "loading";
140+
// } catch (error) {
141+
// return "loading";
142+
// }
143+
// }
144+
145+
// export const shouldShowFallbackImage = (status: Status, fallbackStrategy: FallbackStrategy) =>
146+
// (status !== "loaded" && fallbackStrategy === "beforeLoadOrError") ||
147+
// (status === "failed" && fallbackStrategy === "onError");
148+
149+
// export type UseImageReturn = ReturnType<typeof useImage>;
150+
1151
/**
2152
* Part of this code is taken from @chakra-ui/react package ❤️
3153
*/
4-
import type {ImgHTMLAttributes, MutableRefObject, SyntheticEvent} from "react";
5154

6-
import {useEffect, useRef, useState} from "react";
155+
import type {ImgHTMLAttributes, SyntheticEvent} from "react";
156+
157+
import {useCallback, useEffect, useRef, useState} from "react";
7158
import {useSafeLayoutEffect} from "@nextui-org/use-safe-layout-effect";
8159

9160
type NativeImageProps = ImgHTMLAttributes<HTMLImageElement>;
@@ -46,7 +197,6 @@ type Status = "loading" | "failed" | "pending" | "loaded";
46197
export type FallbackStrategy = "onError" | "beforeLoadOrError";
47198

48199
type ImageEvent = SyntheticEvent<HTMLImageElement, Event>;
49-
50200
/**
51201
* React hook that loads an image in the browser,
52202
* and lets us know the `status` so we can show image
@@ -63,40 +213,44 @@ type ImageEvent = SyntheticEvent<HTMLImageElement, Event>;
63213
* }
64214
* ```
65215
*/
216+
66217
export function useImage(props: UseImageProps = {}) {
67218
const {loading, src, srcSet, onLoad, onError, crossOrigin, sizes, ignoreFallback} = props;
68219

220+
const [status, setStatus] = useState<Status>("pending");
221+
222+
useEffect(() => {
223+
setStatus(src ? "loading" : "pending");
224+
}, [src]);
225+
69226
const imageRef = useRef<HTMLImageElement | null>();
70-
const firstMount = useRef<boolean>(true);
71-
const [status, setStatus] = useState<Status>(() => setImageAndGetInitialStatus(props, imageRef));
72227

73-
useSafeLayoutEffect(() => {
74-
if (firstMount.current) {
75-
firstMount.current = false;
228+
const load = useCallback(() => {
229+
if (!src) return;
76230

77-
return;
78-
}
231+
flush();
79232

80-
setStatus(setImageAndGetInitialStatus(props, imageRef));
233+
const img = new Image();
81234

82-
return () => {
83-
flush();
84-
};
85-
}, [src, crossOrigin, srcSet, sizes, loading]);
235+
img.src = src;
236+
if (crossOrigin) img.crossOrigin = crossOrigin;
237+
if (srcSet) img.srcset = srcSet;
238+
if (sizes) img.sizes = sizes;
239+
if (loading) img.loading = loading;
86240

87-
useEffect(() => {
88-
if (!imageRef.current) return;
89-
imageRef.current.onload = (event) => {
241+
img.onload = (event) => {
90242
flush();
91243
setStatus("loaded");
92244
onLoad?.(event as unknown as ImageEvent);
93245
};
94-
imageRef.current.onerror = (error) => {
246+
img.onerror = (error) => {
95247
flush();
96248
setStatus("failed");
97249
onError?.(error as any);
98250
};
99-
}, [imageRef.current]);
251+
252+
imageRef.current = img;
253+
}, [src, crossOrigin, srcSet, sizes, onLoad, onError, loading]);
100254

101255
const flush = () => {
102256
if (imageRef.current) {
@@ -106,40 +260,25 @@ export function useImage(props: UseImageProps = {}) {
106260
}
107261
};
108262

263+
useSafeLayoutEffect(() => {
264+
/**
265+
* If user opts out of the fallback/placeholder
266+
* logic, let's bail out.
267+
*/
268+
if (ignoreFallback) return undefined;
269+
270+
if (status === "loading") {
271+
load();
272+
}
273+
274+
return () => {
275+
flush();
276+
};
277+
}, [status, load, ignoreFallback]);
278+
109279
/**
110280
* If user opts out of the fallback/placeholder
111281
* logic, let's just return 'loaded'
112282
*/
113283
return ignoreFallback ? "loaded" : status;
114284
}
115-
116-
function setImageAndGetInitialStatus(
117-
props: UseImageProps,
118-
imageRef: MutableRefObject<HTMLImageElement | null | undefined>,
119-
): Status {
120-
const {loading, src, srcSet, crossOrigin, sizes, ignoreFallback} = props;
121-
122-
if (!src) return "pending";
123-
if (ignoreFallback) return "loaded";
124-
125-
const img = document.createElement("img");
126-
127-
img.src = src;
128-
if (crossOrigin) img.crossOrigin = crossOrigin;
129-
if (srcSet) img.srcset = srcSet;
130-
if (sizes) img.sizes = sizes;
131-
if (loading) img.loading = loading;
132-
133-
imageRef.current = img;
134-
if (img.complete && img.naturalWidth) {
135-
return "loaded";
136-
}
137-
138-
return "loading";
139-
}
140-
141-
export const shouldShowFallbackImage = (status: Status, fallbackStrategy: FallbackStrategy) =>
142-
(status !== "loaded" && fallbackStrategy === "beforeLoadOrError") ||
143-
(status === "failed" && fallbackStrategy === "onError");
144-
145-
export type UseImageReturn = ReturnType<typeof useImage>;

0 commit comments

Comments
 (0)