react-native-img-cache icon indicating copy to clipboard operation
react-native-img-cache copied to clipboard

How to handle network error and re-fetch from remote uri

Open gameboyVito opened this issue 8 years ago • 11 comments
trafficstars

Hi, I would like to know how to deal with network error when I am loading images from remote uri. In my case, I have an infinite list to show all images in a listview. Somehow there may be some network errors:

  1. timeout
  2. no network connection
  3. ssl failed

I know I need to handle them inside the onError() callback, but I want to implement a retry function. Something like retry 10 times, if the failed image is loaded successfully, then stop retrying. However, what annoys me is that I am not sure how to refetch the image from remote uri. What I can think is to set the CachedImage to be null and remount it to make it re-render.

Do you have any clue to implement this or add a new feature for us to set props like: timeout, retryNums?

gameboyVito avatar May 08 '17 06:05 gameboyVito

@gameboyVito If you use ImageCache.bust in the onError() callback, it should retry to download the image. Would that solve your use case?

wcandillon avatar May 08 '17 07:05 wcandillon

Hi, dear author. Sorry for the late reply. I have tried it in the onError() callback, but nothing being caught. As I mentioned before, I would like to catch the timeout error. However, it seems like the react-native does not support that feature #7952. Could you add a timeout feature, like opening a public prop for us to set the timeout value?

<CachedImage resizeMode="cover"
         mutable
         source={{uri: this.props.thumb}}
         onError={() => console.log(this.props.name)}
         style={styles.thumbnail}>
</CachedImage>

gameboyVito avatar May 12 '17 16:05 gameboyVito

Hey @wcandillon , I tried your solution of adding bust() in the onError() callback, however it now just enters an infinite reloading loop (e.g. if the image url is invalid).

const handleOnError = (source) => {
  if (source && source.uri){
    console.log('handle on error')
    ImageCache.get().bust(source.uri)
  }
}

Any suggestions on how I handle this (i.e. to only attempt reloading once)?

samw2k00 avatar Aug 10 '17 01:08 samw2k00

You may need to write a sleep function, and use it like await this.sleep(5000) to make it asynchronous.

gameboyVito avatar Aug 10 '17 01:08 gameboyVito

@gameboyVito you are saying that, i should add a sleep before calling the ImageCache.get().bust?

samw2k00 avatar Aug 10 '17 05:08 samw2k00

@samw2k00 Yep, like below:

sleep = (time) => {
    return new Promise((resolve, reject) => setTimeout(() => resolve(), time));
};

handleOnError = (source) => {
  if (source && source.uri){
    console.log('handle on error')
    await sleep(5000);
    ImageCache.get().bust(source.uri)
  }
}

gameboyVito avatar Aug 10 '17 05:08 gameboyVito

Thanks @gameboyVito , that has spaced out the requests (which is good!) - but we're getting this issue repeatedly. Have you seen it before?

nativeEvent: 
{ error: 'Error decoding image data <NSData 0x174a5dfd0; 113 bytes>',
        target: 17796 },

samw2k00 avatar Aug 10 '17 06:08 samw2k00

Yes, I guess it might be caused by unstable network transmission, which makes the downloaded image has binary loss. You may need to check whether the downloaded image is completed.

gameboyVito avatar Aug 10 '17 06:08 gameboyVito

Dope. Any idea how to fix this? We tried adding fs.unlink in the bust() method, to completely clear out the cached file, but that didn't help... i.e.

bust(uri) {
    const cache = this.cache[uri];
    if (cache !== undefined && !cache.immutable) {
        RNFetchBlob.fs.unlink(cache.path)
        cache.path = undefined;
        this.get(uri);
    }
}

samw2k00 avatar Aug 10 '17 06:08 samw2k00

This is where the exception is thrown, at RTCImage source code.

aminroosta avatar Aug 10 '17 09:08 aminroosta

@gameboyVito @aminroosta we found out the issue, this is related to how RNFetchBlob does not respect the 401 status code https://github.com/wkh237/react-native-fetch-blob/issues/267 so i have to explicitly not link the cache, and remove unlink the RNFetchBlib .

cache.task = RNFetchBlob.config({ path }).fetch(method, uri, source.headers);
            cache.task.then((res) => {
                console.log("response info from download", res.respInfo.status, uri)
                cache.downloading = false;
                if (res.respInfo.status === 200) {
                    cache.path = path;
                    this.notify(uri);
                } else {
                    // this is mean its not a 200 response from server, do not link the file to the cache
                    RNFetchBlob.fs.unlink(path);
                }

            }).catch((e) => {
                cache.downloading = false;
                // Parts of the image may have been downloaded already, (see https://github.com/wkh237/react-native-fetch-blob/issues/331)
                RNFetchBlob.fs.unlink(path);
                throw new Error(e)
            });

The second problem is HOW do i tell the custom image component that this download cause error, so i want the onError to be populated. so at the upper level of this component i can handle the Auth issue

samw2k00 avatar Aug 10 '17 09:08 samw2k00