@@ -185,18 +185,10 @@ const Image: React.AbstractComponent<
185
185
) ;
186
186
}
187
187
}
188
- const [ loadedUri , setLoadedUri ] = React . useState ( '' ) ;
189
- const [ state , updateState ] = React . useState ( ( ) => {
190
- const uri = resolveAssetUri ( source ) ;
191
- if ( uri != null ) {
192
- const isLoaded = ImageLoader . has ( uri ) ;
193
- if ( isLoaded ) {
194
- return LOADED ;
195
- }
196
- }
197
- return IDLE ;
198
- } ) ;
199
-
188
+ const { state, loadedUri } = useSource (
189
+ { onLoad, onLoadStart, onLoadEnd, onError } ,
190
+ source
191
+ ) ;
200
192
const [ layout , updateLayout ] = React . useState ( { } ) ;
201
193
const hasTextAncestor = React . useContext ( TextAncestorContext ) ;
202
194
const hiddenImageRef = React . useRef ( null ) ;
@@ -254,53 +246,6 @@ const Image: React.AbstractComponent<
254
246
}
255
247
}
256
248
257
- // Image loading
258
- const uri = resolveAssetUri ( source ) ;
259
- let headers ;
260
- if ( source && typeof source . headers === 'object' ) {
261
- headers = ( ( source . headers : any ) : { [ key : string ] : string } ) ;
262
- }
263
-
264
- React . useEffect ( ( ) => {
265
- if ( uri != null ) {
266
- updateState ( LOADING ) ;
267
- if ( onLoadStart ) onLoadStart ( ) ;
268
-
269
- const requestId = ImageLoader . load (
270
- { uri, headers } ,
271
- function load ( result ) {
272
- updateState ( LOADED ) ;
273
- setLoadedUri ( result . uri ) ;
274
- if ( onLoad ) {
275
- onLoad ( result ) ;
276
- }
277
- if ( onLoadEnd ) {
278
- onLoadEnd ( ) ;
279
- }
280
- } ,
281
- function error ( ) {
282
- updateState ( ERRORED ) ;
283
- if ( onError ) {
284
- onError ( {
285
- nativeEvent : {
286
- error : `Failed to load resource ${ uri } (404)`
287
- }
288
- } ) ;
289
- }
290
- if ( onLoadEnd ) {
291
- onLoadEnd ( ) ;
292
- }
293
- }
294
- ) ;
295
-
296
- const effectCleanup = ( ) => {
297
- if ( requestId ) ImageLoader . release ( requestId ) ;
298
- } ;
299
-
300
- return effectCleanup ;
301
- }
302
- } , [ updateState , onError , onLoad , onLoadEnd , onLoadStart , uri , headers ] ) ;
303
-
304
249
return (
305
250
< View
306
251
{ ...rest }
@@ -351,6 +296,83 @@ ImageWithStatics.queryCache = function (uris) {
351
296
return ImageLoader . queryCache ( uris ) ;
352
297
} ;
353
298
299
+ type UseSourceParams = {
300
+ onLoad ?: Function ,
301
+ onLoadStart ?: Function ,
302
+ onLoadEnd ?: Function ,
303
+ onError ?: Function
304
+ } ;
305
+
306
+ /**
307
+ * Image loading/state management hook
308
+ * @param params
309
+ * @param source
310
+ * @returns {{state: string, uri: string} }
311
+ */
312
+ const useSource = (
313
+ { onLoad, onLoadStart, onLoadEnd, onError } : UseSourceParams ,
314
+ source : ?Source
315
+ ) : { state : string , uri : string } = > {
316
+ const [ loadedUri , setLoadedUri ] = React . useState ( '' ) ;
317
+ const [ state , updateState ] = React . useState ( ( ) => {
318
+ const uri = resolveAssetUri ( source ) ;
319
+ if ( uri != null ) {
320
+ const isLoaded = ImageLoader . has ( uri ) ;
321
+ if ( isLoaded ) return LOADED ;
322
+ }
323
+ return IDLE ;
324
+ } ) ;
325
+
326
+ const loadInput = React . useRef ( null ) ;
327
+
328
+ React . useEffect ( ( ) => {
329
+ const uri = resolveAssetUri ( source ) ;
330
+ if ( uri == null ) return ;
331
+
332
+ let headers ;
333
+ if ( source && typeof source . headers === 'object' ) {
334
+ headers = ( ( source . headers : any ) : { [ key : string ] : string } ) ;
335
+ }
336
+
337
+ const nextInput = { uri , headers } ;
338
+ const currentInput = loadInput . current ;
339
+ if ( JSON . stringify ( nextInput ) === JSON . stringify ( currentInput ) ) return ;
340
+
341
+ updateState ( LOADING ) ;
342
+ if ( onLoadStart ) onLoadStart ( ) ;
343
+
344
+ loadInput . current = nextInput ;
345
+ const requestId = ImageLoader . load (
346
+ nextInput ,
347
+ function load ( result ) {
348
+ updateState ( LOADED ) ;
349
+ setLoadedUri ( result . uri ) ;
350
+ if ( onLoad ) onLoad ( result ) ;
351
+ if ( onLoadEnd ) onLoadEnd ( ) ;
352
+ } ,
353
+ function error ( ) {
354
+ updateState ( ERRORED ) ;
355
+ if ( onError ) {
356
+ onError ( {
357
+ nativeEvent : {
358
+ error : `Failed to load resource ${ uri } (404)`
359
+ }
360
+ } ) ;
361
+ }
362
+ if ( onLoadEnd ) onLoadEnd ( ) ;
363
+ }
364
+ ) ;
365
+
366
+ const effectCleanup = ( ) => ImageLoader . release ( requestId ) ;
367
+ return effectCleanup ;
368
+ } , [ updateState , onError , onLoad , onLoadEnd , onLoadStart , source ] ) ;
369
+
370
+ return {
371
+ state,
372
+ loadedUri
373
+ } ;
374
+ } ;
375
+
354
376
const styles = StyleSheet . create ( {
355
377
root : {
356
378
flexBasis : 'auto' ,
0 commit comments