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
+
1
151
/**
2
152
* Part of this code is taken from @chakra-ui/react package ❤️
3
153
*/
4
- import type { ImgHTMLAttributes , MutableRefObject , SyntheticEvent } from "react" ;
5
154
6
- import { useEffect , useRef , useState } from "react" ;
155
+ import type { ImgHTMLAttributes , SyntheticEvent } from "react" ;
156
+
157
+ import { useCallback , useEffect , useRef , useState } from "react" ;
7
158
import { useSafeLayoutEffect } from "@nextui-org/use-safe-layout-effect" ;
8
159
9
160
type NativeImageProps = ImgHTMLAttributes < HTMLImageElement > ;
@@ -46,7 +197,6 @@ type Status = "loading" | "failed" | "pending" | "loaded";
46
197
export type FallbackStrategy = "onError" | "beforeLoadOrError" ;
47
198
48
199
type ImageEvent = SyntheticEvent < HTMLImageElement , Event > ;
49
-
50
200
/**
51
201
* React hook that loads an image in the browser,
52
202
* and lets us know the `status` so we can show image
@@ -63,40 +213,44 @@ type ImageEvent = SyntheticEvent<HTMLImageElement, Event>;
63
213
* }
64
214
* ```
65
215
*/
216
+
66
217
export function useImage ( props : UseImageProps = { } ) {
67
218
const { loading, src, srcSet, onLoad, onError, crossOrigin, sizes, ignoreFallback} = props ;
68
219
220
+ const [ status , setStatus ] = useState < Status > ( "pending" ) ;
221
+
222
+ useEffect ( ( ) => {
223
+ setStatus ( src ? "loading" : "pending" ) ;
224
+ } , [ src ] ) ;
225
+
69
226
const imageRef = useRef < HTMLImageElement | null > ( ) ;
70
- const firstMount = useRef < boolean > ( true ) ;
71
- const [ status , setStatus ] = useState < Status > ( ( ) => setImageAndGetInitialStatus ( props , imageRef ) ) ;
72
227
73
- useSafeLayoutEffect ( ( ) => {
74
- if ( firstMount . current ) {
75
- firstMount . current = false ;
228
+ const load = useCallback ( ( ) => {
229
+ if ( ! src ) return ;
76
230
77
- return ;
78
- }
231
+ flush ( ) ;
79
232
80
- setStatus ( setImageAndGetInitialStatus ( props , imageRef ) ) ;
233
+ const img = new Image ( ) ;
81
234
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 ;
86
240
87
- useEffect ( ( ) => {
88
- if ( ! imageRef . current ) return ;
89
- imageRef . current . onload = ( event ) => {
241
+ img . onload = ( event ) => {
90
242
flush ( ) ;
91
243
setStatus ( "loaded" ) ;
92
244
onLoad ?.( event as unknown as ImageEvent ) ;
93
245
} ;
94
- imageRef . current . onerror = ( error ) => {
246
+ img . onerror = ( error ) => {
95
247
flush ( ) ;
96
248
setStatus ( "failed" ) ;
97
249
onError ?.( error as any ) ;
98
250
} ;
99
- } , [ imageRef . current ] ) ;
251
+
252
+ imageRef . current = img ;
253
+ } , [ src , crossOrigin , srcSet , sizes , onLoad , onError , loading ] ) ;
100
254
101
255
const flush = ( ) => {
102
256
if ( imageRef . current ) {
@@ -106,40 +260,25 @@ export function useImage(props: UseImageProps = {}) {
106
260
}
107
261
} ;
108
262
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
+
109
279
/**
110
280
* If user opts out of the fallback/placeholder
111
281
* logic, let's just return 'loaded'
112
282
*/
113
283
return ignoreFallback ? "loaded" : status ;
114
284
}
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