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