8
8
* @flow
9
9
*/
10
10
11
- import type { ImageProps } from './types' ;
11
+ import type { ImageProps , Source } from './types' ;
12
12
13
13
import * as React from 'react' ;
14
14
import createElement from '../createElement' ;
@@ -317,9 +317,10 @@ type UseSourceParams = {
317
317
const useSource = (
318
318
{ onLoad, onLoadStart, onLoadEnd, onError } : UseSourceParams ,
319
319
source : ?Source
320
- ) : { state : string , uri : string } = > {
320
+ ) : { state : string , loadedUri : string } = > {
321
+ const lastLoadedSource = React . useRef ( ) ;
321
322
const [ loadedUri , setLoadedUri ] = React . useState ( '' ) ;
322
- const [ state , updateState ] = React . useState ( ( ) => {
323
+ const [ state , setState ] = React . useState ( ( ) => {
323
324
const uri = resolveAssetUri ( source ) ;
324
325
if ( uri != null ) {
325
326
const isLoaded = ImageLoader . has ( uri ) ;
@@ -328,39 +329,50 @@ const useSource = (
328
329
return IDLE ;
329
330
} ) ;
330
331
331
- const loadInput = React . useRef ( null ) ;
332
-
333
- React . useEffect ( ( ) => {
332
+ // This object would only change when load related fields change
333
+ // We try to maintain strict object reference to prevent the effect hook running due to object change
334
+ const stableSource = React . useMemo ( ( ) => {
334
335
const uri = resolveAssetUri ( source ) ;
335
- if ( uri == null ) return ;
336
+ if ( uri == null ) {
337
+ lastLoadedSource . current = null ;
338
+ return null ;
339
+ }
336
340
337
341
let headers ;
338
342
if ( source && typeof source . headers === 'object' ) {
339
343
headers = ( ( source . headers : any ) : { [ key : string ] : string } ) ;
340
344
}
341
345
342
346
const nextInput = { uri , headers } ;
343
- const currentInput = loadInput . current ;
344
- if ( JSON . stringify ( nextInput ) === JSON . stringify ( currentInput ) ) return ;
347
+ if (
348
+ JSON . stringify ( nextInput ) !== JSON . stringify ( lastLoadedSource . current )
349
+ ) {
350
+ lastLoadedSource . current = nextInput ;
351
+ }
352
+
353
+ return lastLoadedSource . current ;
354
+ } , [ source ] ) ;
355
+
356
+ React . useEffect ( ( ) => {
357
+ if ( stableSource == null ) return ;
345
358
346
- updateState ( LOADING ) ;
359
+ setState ( LOADING ) ;
347
360
if ( onLoadStart ) onLoadStart ( ) ;
348
361
349
- loadInput . current = nextInput ;
350
362
const requestId = ImageLoader . load (
351
- nextInput ,
363
+ stableSource ,
352
364
function load ( result ) {
353
- updateState ( LOADED ) ;
365
+ setState ( LOADED ) ;
354
366
setLoadedUri ( result . uri ) ;
355
367
if ( onLoad ) onLoad ( result ) ;
356
368
if ( onLoadEnd ) onLoadEnd ( ) ;
357
369
} ,
358
370
function error ( ) {
359
- updateState ( ERRORED ) ;
371
+ setState ( ERRORED ) ;
360
372
if ( onError ) {
361
373
onError ( {
362
374
nativeEvent : {
363
- error : `Failed to load resource ${ uri } (404)`
375
+ error : `Failed to load resource ${ stableSource . uri } (404)`
364
376
}
365
377
} ) ;
366
378
}
@@ -370,7 +382,7 @@ const useSource = (
370
382
371
383
const effectCleanup = ( ) => ImageLoader . release ( requestId ) ;
372
384
return effectCleanup ;
373
- } , [ updateState , onError , onLoad , onLoadEnd , onLoadStart , source ] ) ;
385
+ } , [ onError , onLoad , onLoadEnd , onLoadStart , stableSource ] ) ;
374
386
375
387
return {
376
388
state,
0 commit comments