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
-
151
1
/**
152
2
* Part of this code is taken from @chakra-ui/react package ❤️
153
3
*/
154
4
155
5
import type { ImgHTMLAttributes , SyntheticEvent } from "react" ;
156
6
157
- import { useCallback , useEffect , useRef , useState } from "react" ;
158
- import { useSafeLayoutEffect } from "@nextui-org/use-safe-layout-effect " ;
7
+ import { useRef , useState , useEffect , MutableRefObject } from "react" ;
8
+ import { useIsHydrated } from "@nextui-org/react-utils " ;
159
9
160
10
type NativeImageProps = ImgHTMLAttributes < HTMLImageElement > ;
161
11
@@ -215,42 +65,29 @@ type ImageEvent = SyntheticEvent<HTMLImageElement, Event>;
215
65
*/
216
66
217
67
export function useImage ( props : UseImageProps = { } ) {
218
- const { loading, src, srcSet, onLoad, onError, crossOrigin, sizes, ignoreFallback} = props ;
219
-
220
- const [ status , setStatus ] = useState < Status > ( "pending" ) ;
221
-
222
- useEffect ( ( ) => {
223
- setStatus ( src ? "loading" : "pending" ) ;
224
- } , [ src ] ) ;
225
-
226
- const imageRef = useRef < HTMLImageElement | null > ( ) ;
227
-
228
- const load = useCallback ( ( ) => {
229
- if ( ! src ) return ;
68
+ const { onLoad, onError, ignoreFallback} = props ;
230
69
231
- flush ( ) ;
70
+ const isHydrated = useIsHydrated ( ) ;
232
71
233
- const img = new Image ( ) ;
72
+ const imageRef = useRef < HTMLImageElement | null > ( isHydrated ? new Image ( ) : null ) ;
234
73
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 ;
74
+ const [ status , setStatus ] = useState < Status > ( ( ) =>
75
+ isHydrated ? setImageAndGetInitialStatus ( props , imageRef ) : "pending" ,
76
+ ) ;
240
77
241
- img . onload = ( event ) => {
78
+ useEffect ( ( ) => {
79
+ if ( ! imageRef . current ) return ;
80
+ imageRef . current . onload = ( event ) => {
242
81
flush ( ) ;
243
82
setStatus ( "loaded" ) ;
244
83
onLoad ?.( event as unknown as ImageEvent ) ;
245
84
} ;
246
- img . onerror = ( error ) => {
85
+ imageRef . current . onerror = ( error ) => {
247
86
flush ( ) ;
248
87
setStatus ( "failed" ) ;
249
88
onError ?.( error as any ) ;
250
89
} ;
251
-
252
- imageRef . current = img ;
253
- } , [ src , crossOrigin , srcSet , sizes , onLoad , onError , loading ] ) ;
90
+ } , [ imageRef . current ] ) ;
254
91
255
92
const flush = ( ) => {
256
93
if ( imageRef . current ) {
@@ -260,25 +97,40 @@ export function useImage(props: UseImageProps = {}) {
260
97
}
261
98
} ;
262
99
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
-
279
100
/**
280
101
* If user opts out of the fallback/placeholder
281
102
* logic, let's just return 'loaded'
282
103
*/
283
104
return ignoreFallback ? "loaded" : status ;
284
105
}
106
+
107
+ function setImageAndGetInitialStatus (
108
+ props : UseImageProps ,
109
+ imageRef : MutableRefObject < HTMLImageElement | null | undefined > ,
110
+ ) : Status {
111
+ const { loading, src, srcSet, crossOrigin, sizes, ignoreFallback} = props ;
112
+
113
+ if ( ! src ) return "pending" ;
114
+ if ( ignoreFallback ) return "loaded" ;
115
+
116
+ const img = new Image ( ) ;
117
+
118
+ img . src = src ;
119
+ if ( crossOrigin ) img . crossOrigin = crossOrigin ;
120
+ if ( srcSet ) img . srcset = srcSet ;
121
+ if ( sizes ) img . sizes = sizes ;
122
+ if ( loading ) img . loading = loading ;
123
+
124
+ imageRef . current = img ;
125
+ if ( img . complete && img . naturalWidth ) {
126
+ return "loaded" ;
127
+ }
128
+
129
+ return "loading" ;
130
+ }
131
+
132
+ export const shouldShowFallbackImage = ( status : Status , fallbackStrategy : FallbackStrategy ) =>
133
+ ( status !== "loaded" && fallbackStrategy === "beforeLoadOrError" ) ||
134
+ ( status === "failed" && fallbackStrategy === "onError" ) ;
135
+
136
+ export type UseImageReturn = ReturnType < typeof useImage > ;
0 commit comments