Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retry functionality #204

Open
MrLoh opened this issue May 8, 2018 · 17 comments
Open

Retry functionality #204

MrLoh opened this issue May 8, 2018 · 17 comments

Comments

@MrLoh
Copy link

MrLoh commented May 8, 2018

This is such a must have for React native, probably should be just part of core.

However I’ve observed that sometimes images don’t work in my app just to then be fine after restarting it. It’s super hard to debug, because I can’t reproduce it. Seems to be happening quite randomly.

Maybe someone else has already encountered this or some idea how to try refetching an Image if it fails. I’ll keep trying ideas but debugging cycles are weeks because I can just try something and then see wether the issue still appears in the following weeks as I use my own app.

@MrLoh
Copy link
Author

MrLoh commented May 8, 2018

I had tried before to implement retrying by passing a number to fast image that I store in state of a Wrapper and then increment on error. This doesn’t have seemed to fix the error. Maybe it doesn’t trigger a rerender, maybe the native underlying don’t refetch on rerender. A little hard to tell without in depth knowledge about the inner workings. Ideally the library could retry fetching internally.

@DylanVann
Copy link
Owner

DylanVann commented May 8, 2018

I think there's a ways to go before it could be merged into core (#12). I do think that would be nice though. Glad you've found it useful.

Yeah this is tricky, if there's no way to reproduce this issue. There is an issue open for better error handling. Maybe the least we could do is stringify whatever SDWebImage or Glide fails with as one field of the onError callback. You could log that and it might help. (#200)

@MrLoh
Copy link
Author

MrLoh commented May 8, 2018

yeah better error reporting would certainly help. Do you know wether rerendering a component will actually refetch an image, I haven't read the sourcecode yet, but one think that could be part of the issue is that once an image has failed rerendering will just resolve the failure from cache until the app is restarted, at least I don't have the feeling that rerendering actually retries fetching images.

@DylanVann
Copy link
Owner

It will retry if it fails and calls setSource is run again. It uses the SDWebImageRetryFailed option for SDWebImage, assuming we're talking about iOS, are we?

Getting setSource to run again would be slightly tricky. If the component is unmounted and mounted again that would do it.

@MrLoh
Copy link
Author

MrLoh commented May 9, 2018

I’ve observed the behavior on both iOS and android. I guess I could force unmounte the image, by just rendering null in between retries.

Sent with GitHawk

@DylanVann
Copy link
Owner

Does the image exist on the server when you're trying to display it? Is it a timing issue?

@MrLoh
Copy link
Author

MrLoh commented May 9, 2018

yeah it seems completely random, sometimes images just fail but upon restarting the app they are there and they could fail again later. Could have something todo with our server, but nothing obvious, it's really hard to say.

@DylanVann
Copy link
Owner

I've never seen that. I'll try to add that error logging tonight so you can see what's going on.

@MrLoh
Copy link
Author

MrLoh commented May 9, 2018

thanks, and I asked my colleague to look into wether it could be a server side issue.

@MrLoh
Copy link
Author

MrLoh commented May 17, 2018

@DylanVann how is it going with that error logging, did you get to it? Couldn’t find any info yet and wanted to set this up.

@pppluto
Copy link

pppluto commented Nov 16, 2019

same issue here. for us, it seems happened when the network is bad, after i changed the network(let me say wifi -> 4g), the failed image wont reload.

@tatiesmars
Copy link

tatiesmars commented Dec 17, 2019

Android seem to reload images that fail to load a first time.

Concerning iOS, the image never reloads if it fail unless the component is refresh


It will be much appreciate to have this support. Seem to be a very basic image function

@nandorojo
Copy link

nandorojo commented Sep 15, 2021

Currently I'm trying this:

const [errorKey, setErrorKey] = useState(0)

const onError = () => setErrorKey(key => key + 1)

return <FastImage source={{ uri }} onLoadError={onError} key={errorKey + uri} />

That triggers a re-render whenever onLoadError is called. @DylanVann should this work on iOS to retry?

@DylanVann
Copy link
Owner

DylanVann commented Sep 15, 2021

@nandorojo Yes that will work. You could potentially build a wrapper component that allows for clicking to retry manually using something like that and the onLoadError callback.

@nandorojo
Copy link

nandorojo commented Sep 15, 2021

Good idea. However, if someone taps it, and it has already loaded properly, do you have any thoughts for not re-rendering in that case? Or does it not matter, because it'll just get the image from the cache?

@nandorojo
Copy link

nandorojo commented Sep 16, 2021

Here's a more elaborate retry functionality I made. @DylanVann lmk how this looks.

const REFETCH_FAILED_IMAGE_EVERY_MS = 3_000

const loadedImages = useRef<Record<string, boolean>>({})

const [imageErrorKey, setImageErrorKey] = useState(0)

const onReloadImage = useCallback(() => {
  setImageErrorKey((key) => key + 1)
}, [])
const onImageError = useCallback(() => {
  onReloadImage()
}, [onReloadImage])

const onPressImage = useCallback(() => {
  const hasLoaded = imageUri && loadedImages.current[imageUri]
  if (hasLoaded) {
    return
  }

  onReloadImage()
}, [imageUri, onReloadImage])

const refetchImageInterval = useRef(0)

const onImageLoadStart = useCallback(() => {
  if (imageUri) {
    loadedImages.current[imageUri] = false
    clearInterval(refetchImageInterval.current)
    refetchImageInterval.current = setInterval(() => {
      if (!loadedImages.current[imageUri]) {
        onReloadImage()
      }
    }, REFETCH_FAILED_IMAGE_EVERY_MS)
  }
}, [imageUri, onReloadImage])

const onImageLoadEnd = useCallback(() => {
  if (imageUri) {
    clearInterval(refetchImageInterval.current)
    loadedImages.current[imageUri] = true
  }
}, [imageUri])

useEffect(function unmount() {
  return () => {
    clearInterval(refetchImageInterval.current)
  }
}, [])

return (
  <Pressable onPress={onPressImage}>
    <FastImage
      style={StyleSheet.absoluteFillObject}
      source={{
        uri: imageUri,
        priority: 'high'
      }}
      onError={onImageError}
      key={`image-${imageUri}-${imageErrorKey}`}
      onLoadStart={onLoadStatrt}
      onLoadEnd={onLoadEnd}
    />
  </Pressable>
)

One weird quirk: I noticed onLoadStart always fires twice. Not sure if this is a big deal. I'm clearing any open interval in onImageLoadStart in case.

@zholmes1
Copy link

zholmes1 commented Oct 6, 2021

Hey all, just wanted to share a pretty simple solution I came up with that can be a drop-in replacement for the FastImage component. This is super useful for me since I use a serverless function to resize my images after the user uploads them to storage. So there was no way for the phone to know exactly when the resize was complete.

export default ({
  width,
  height,
  maxRetries = 5,
  ...props
}: FastImageProps & {
  width: number
  height: number
  maxRetries?: number
}) => {
  const [errorCount, setErrorCount] = useState(0)
  const [visible, setVisible] = useState(true)

  return (
    <View style={{ width, height }}>
      {visible && (
        <FastImage
          {...props}
          style={[props.style, { width, height }]}
          onError={() => {
            if (errorCount < maxRetries) {
              setErrorCount(errorCount + 1)
              setVisible(false)
              setTimeout(() => setVisible(true), 500)
            }
          }}
        />
      )}
    </View>
  )
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
6 participants