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

Image Component will not update correctly when passing in new url #9195

Closed
jnrepo opened this issue Aug 3, 2016 · 62 comments
Closed

Image Component will not update correctly when passing in new url #9195

jnrepo opened this issue Aug 3, 2016 · 62 comments
Labels
Bug Component: Image Good first issue Interested in collaborating? Take a stab at fixing one of these issues. Platform: iOS iOS applications.

Comments

@jnrepo
Copy link

jnrepo commented Aug 3, 2016

So I have this basic network image component. It will probably look similar to the one in the Image Component documents. The question is that when I switch the url, for the image, the image itself will not change to the new one, although it seems to be calling the render function again.

Device: iOS
Using: Mac
React-Native: 0.26.3

import React from 'react'
import {
  View,
  Image
} from 'react-native'

const NetworkImage = React.createClass({
  getInitialState() {
    return {
      source: this.props.source
    }
  },
  componentWillReceiveProps(newProps) {
    if (newProps.source !== this.props.source) {
      this.setState({
        source: newProps.source
      })
    }
  },
  render() {
    console.log('source: ', this.props)
    return (
      <Image
        style={this.props.style}
        source={this.state.source}
        onLoad={() => {
          console.log('loaded image!')
        }}
        onLoadStart={() => {
          console.log('load starting')
        }}/>)
  }
})

module.exports = NetworkImage
@npomfret
Copy link
Contributor

npomfret commented Aug 8, 2016

I've experienced that same issue. Try adding a key={this.state.source.uri} property to the image

@eladgel
Copy link

eladgel commented Aug 16, 2016

@npomfret this solution works for me

@jnrepo
Copy link
Author

jnrepo commented Aug 17, 2016

@npomfret unfortunately the solution didn't work for me. What ended up working was putting a timeout around setState within the componentWillReceiveProps. Not the most ideal solution :(

Code:

import React from 'react'
import {
  Image
} from 'react-native'

const TimerMixin = require('react-timer-mixin')

const NetworkImage = React.createClass({
  mixins: [TimerMixin],
  getInitialState() {
    return {
      source: this.props.source
    }
  },
  componentWillReceiveProps(newProps) {
    if (newProps.source.uri !== this.props.source.uri) {
      this.setTimeout(() => {
        this.setState({
          source: newProps.source
        })
      }, 100)
    }
  },
  render() {
    return (
      <Image
        style={this.props.style}
        source={this.state.source}
        onLoad={() => {
          console.log('loaded image!')
        }}
        onLoadStart={() => {
          console.log('load starting')
        }}/>)
  }
})

module.exports = NetworkImage

@npomfret
Copy link
Contributor

How strange! This feels like a fairly serious bug. Can you reproduce it? What might be helpful is a stand alone git repo that illustrates the problem.

@charpeni
Copy link
Contributor

@facebook-github-bot label Icebox

@charpeni
Copy link
Contributor

Hi there! This issue is being closed because it has been inactive for a while.

But don't worry, it will live on with ProductPains! Check out its new home: https://productpains.com/post/react-native/image-component-will-not-update-correctly-when-passing-in-new-url

ProductPains helps the community prioritize the most important issues thanks to its voting feature.
It is easy to use - just login with GitHub.

Also, if this issue is a bug, please consider sending a PR with a fix.
We're a small team and rely on the community for bug fixes of issues that don't affect fb apps.

@charpeni
Copy link
Contributor

@facebook-github-bot close

@facebook-github-bot
Copy link
Contributor

@charpeni tells me to close this issue. If you think it should still be opened let us know why.

@facebook-github-bot facebook-github-bot added Ran Commands One of our bots successfully processed a command. Icebox labels Nov 14, 2016
mrdeanbw pushed a commit to mrdeanbw/ReactNative_HoopsApp that referenced this issue Aug 7, 2017
For some reason, real devices (non-simulator) are not updating tab-bar
images when they change from active to not-active.
Could be related to this bug?
facebook/react-native#9195

We add a key to force the <Image> element to re-mount when the source
changes
@wcandillon
Copy link
Contributor

@npomfret I learned about the bug the hard way 😅 . It quite a serious one that probably should be reopened.

@charpeni charpeni reopened this Jan 27, 2018
@charpeni
Copy link
Contributor

@wcandillon done. Can you create a replication of the bug on Snack? https://snack.expo.io/

@wcandillon
Copy link
Contributor

@charpeni: I wrote a snack here: https://snack.expo.io/@wcandillon/progressive-image-loading if you use such component (where uri is in the state and we change the value of the RN Image source) on a large collection of images, sometimes it will not refresh the image (5% of the time). Using the workaround above with key solves the issue but to creates a flicker in the image.

The way I was able to work around this issue to to superpose the new Image component on top of the old one instead.

@hramos hramos added Good first issue Interested in collaborating? Take a stab at fixing one of these issues. Platform: iOS iOS applications. and removed Icebox Ran Commands One of our bots successfully processed a command. labels Mar 5, 2018
@prakharrke
Copy link

prakharrke commented Mar 21, 2018

It is not restricted to changing the URI. Even if I change the image at the same URI, it still loads the previously placed image. I'll soon enough put reproduction steps. If I use Google Postman to do a simple GET request to the URI, I get the updated image. But Image component still loads the old image.

@prakharrke
Copy link

I looked into it a lot. As it turns out, React native does not even send a GET request to the server for the image. Which makes me think that it is caching the image somewhere.

@prakharrke
Copy link

I was going through react native node modules and in react-native->Libraries->Image->ImageSource.js, following is defined.
`/**

Copyright (c) 2015-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
@providesmodule ImageSource
@flow
*/
'use strict';
// This is to sync with ImageSourcePropTypes.js.

type ImageURISource = {
uri?: string,
bundle?: string,
method?: string,
headers?: Object,
body?: string,
cache?: 'default' | 'reload' | 'force-cache' | 'only-if-cached',
width?: number,
height?: number,
scale?: number,
};

export type ImageSource = ImageURISource | number | Array;
`

Which makes me think that there is prop called 'cache', possible values to which are given above. I added to source prop like follows.
<Image source={{uri : myImage, cache: 'reload'}} />

According to its definition, it should always reload the image from the server and not the cache. But it is not working.

@shohailahm
Copy link

its a serious issue. i am facing it too

@wcandillon
Copy link
Contributor

@shohailahm I discuss a workaround for this bug in the following medium story: https://hackernoon.com/5-things-to-know-about-images-react-native-69be41d2a9ee

@shohailahm
Copy link

@wcandillon without expo there isnt any workaround?

@wcandillon
Copy link
Contributor

@shohailahm It might sound super hacky but the T workaround is to superpose new images on top of the "old" ones. This doesn't depend on expo. You can find a example here: https://github.com/wcandillon/react-native-expo-image-cache/blob/master/src/Image.js#L86

@jimmy-kwan
Copy link

jimmy-kwan commented Jul 5, 2018

found a solution by adapting the react native source code handling the loading of image, code below is for iOS as that is what I need for now, have not tried on Android as i think the source code handling loading of image different for both platform:

  • If you are loading another image file from your local assets:
    Add this somewhere in your code

    const resolveAssetSource = require('resolveAssetSource');

    Then at the point that you need to load another image using the same component, do the following:

    let maps = [resolveAssetSource(new_local_image_name)];
    this.refs["reference_string_to_your_image_component"].setNativeProps({source: maps});

  • If you are loading another image from the network, add the following codes at the point that you need to load the image using the same component:

    maps = [{uri: "https://network_address_to_the_image", width: image_width_int, height: image_height_int}];
    this.refs["reference_string_to_your_image_component"].setNativeProps({source: maps});

@fidelsam1992
Copy link

When all other solutions didn't work for us ... we tried changing http to https and it worked perfectly without needed to add key.

Make sure that you are reading from https when running on iOS.

@tobiasr
Copy link

tobiasr commented Sep 1, 2020

I think I've been able to reproduce this issue in a new Snack, here:
https://snack.expo.io/@rundbom/image-should-update

By using lorem picsum as a source a new remote image is available on each request to the same URI.

We force the image to rerender by updating the key prop.

The image doesn't change even though cache: 'reload' is used.

This might be expected behaviour though?

One idea on fixing this (which I could send a PR on) would be to have request.cachePolicy impact if a cached image is used in RCTImageLoader._loadImageOrDataWithURLRequest. Code location:

BOOL cacheResult = [loadHandler respondsToSelector:@selector(shouldCacheLoadedImages)] ?
[loadHandler shouldCacheLoadedImages] : YES;

Would be glad to help out on this issue!

@BruceSuperProgramer
Copy link

Guys just simply assign <Image key={diffrent key} /> with a different key. Every time the screen rerendered if it found key is a new one it will rerender this component again even it has the same URL.

@tobiasr
Copy link

tobiasr commented Sep 12, 2020

Guys just simply assign <Image key={diffrent key} /> with a different key. Every time the screen rerendered if it found key is a new one it will rerender this component again even it has the same URL.

So, my example in Snack is contradicting exactly this. Given the same URL a new key will not "reload" the image (the component will be rerendered but the image won't be refetched). Tampering with the URL will reload the image. This is however in contradiction with the title of this issue, so it might be OT (the discussion have been talking about this case for a while though)?

On IOS the image component seems to have support for the cache: 'reload' setting. The native code doesn't seem to do anything to support that setting though. So my question was if the IOS specific code should respect the setting, or maybe if the setting should be removed or documented as not working in this case?

@BruceSuperProgramer
Copy link

@tobiasr In that case, is there any possibility to fix it by adding? new Date() right after the image URL. Something like that https://image/image.jpg?${new Date()}

@tobiasr
Copy link

tobiasr commented Sep 13, 2020

@BruceSuperProgramer That will fix the issue.

For me, the question is regarding this documentation for IOS:
https://reactnative.dev/docs/images#cache-control-ios-only

My Snack, too me, is proving that these settings (at least cache: 'reload') have no effect on the load behaviour of the data on the other side of the URI.

So either an issue should be fixed or perhaps the settings should be removed/documented as not working?

@SahanDll
Copy link

@tobiasr In that case, is there any possibility to fix it by adding? new Date() right after the image URL. Something like that https://image/image.jpg?${new Date()}

Worked for me

                    source={{
                        uri: REST_ENDPOINT_BASE + "/document-view/sample.jpg?date=" + moment().valueOf()
                        cache: "reload",
                        headers: {
                            Pragma: "no-cache",
                        }

@tobiasr
Copy link

tobiasr commented Oct 20, 2020

@tobiasr In that case, is there any possibility to fix it by adding? new Date() right after the image URL. Something like that https://image/image.jpg?${new Date()}

Worked for me

                    source={{
                        uri: REST_ENDPOINT_BASE + "/document-view/sample.jpg?date=" + moment().valueOf()
                        cache: "reload",
                        headers: {
                            Pragma: "no-cache",
                        }

I think it it should work without cache: "reload" as well, right? As I've tried to explain above, that setting isn't used on the image component.

@Vivekmanu4343
Copy link

Vivekmanu4343 commented Oct 25, 2020

Even I'm facing this issue and any of the above solutions did not help me in any way. The Text component is printing proper URI during each render but the image still shows up the same.

{thumbnails.map((thumbnail) => (
                  <View>
                  <TouchableOpacity onPress={() => setSelectedThumbnail(thumbnails[index])}>
                    < Image key={thumbnail} source={{ uri: thumbnail, cache: "reload",
                        headers: {
                            Pragma: "no-cache",
                        } }} style={{ width: 30, height: 30, marginLeft: 10 }} />
                  </TouchableOpacity> 
                <Text key={thumbnail} style={{fontSize:7}}>{thumbnail}</Text>
                </View>
                  ))}

Please look at the image and its URI.
image

Note: The image is rendering properly when i just make any small change and expo refresh the page.

@sbarbour-93
Copy link

sbarbour-93 commented Oct 30, 2020

Happy Friday,

I might have a solution - but it ain't pretty...

After being severely p***ed off by this problem the past few days and trying everything from cache: reload and different image libraries like fast-image to no avail I happened to make an important observation after my [what felt like] 1000th time of trying to overcome this tenacious little bug.

I noticed that if I quickly dismounted and re-mounted the component which contained the Image component that the original image would load - this didn't stack up with my original hypothesis (shared by many on this thread) that unmounting and remounting was causing the image component to work as expected.

So, instead of setting my image's URI to the prop which I passed into my component I set it to a state variable. I then setup a useEffect hook which would execute whenever the URI prop changed - but instead of immediately updating the state I used the setTimeout function and updated the state's copy of the URI after a few seconds ... and it worked as expected.

Screenshot 2020-10-30 at 17 48 59

@sandyvb
Copy link

sandyvb commented Apr 16, 2021

Tried all of the suggestions above with no success :(

@atosh502
Copy link

I've experienced that same issue. Try adding a key={this.state.source.uri} property to the image

For me it worked on I combined key with date ie. key=${this.state.source.uri}${new Date()}

@ashadnasim52
Copy link

I've experienced that same issue. Try adding a key={this.state.source.uri} property to the image

For me it worked on I combined key with date ie. key=${this.state.source.uri}${new Date()}

It worked :)

@bilal-korir
Copy link

I've experienced that same issue. Try adding a key={this.state.source.uri} property to the image

Brilliant! thank you @npomfret

@Leleka14
Copy link

Leleka14 commented Dec 9, 2021

for me it forked to randomly assign imageIndex only once when component is mounted and if image is changed, it only takes to refresh screen, may not be the best solution, but at least image is not flickering

const randomValue = useMemo(()=>Math.random(), [])

<Image src={{uri: https://image/image.jpg?id={randomValue}}}/>

@vipulchakravarthy
Copy link

I've experienced that same issue. Try adding a key={this.state.source.uri} property to the image

OMG!!!!!!! You really saved my day @npomfret

@VictorioMolina
Copy link

VictorioMolina commented Jun 18, 2022

The problem still persists. Updating the image component via dynamic keys is kinda weird: https://snack.expo.dev/xW149Kxkv

@OsidAbu-alrub
Copy link

Issue still happens, I fixed it by using conditional rendering.

So if there's a file uploading, I remove the Image component and replace it with a loader. Then add it again once the image is uploaded successfully to the server.

@ricardo42alves
Copy link

ricardo42alves commented Mar 20, 2023

I'm still having this issue on android devices with the "react-native": "0.70.5", does anyone know which version this was fixed?

@CalvinJamesHeath
Copy link

<img src='yourUrl' key={Math.random()}/>

@skyllex94
Copy link

skyllex94 commented May 22, 2024

Here's an example of how to fix it in a functional component

{list.map((path, idx) => (
<Image key={path} source={{uri: path}} />
))}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Component: Image Good first issue Interested in collaborating? Take a stab at fixing one of these issues. Platform: iOS iOS applications.
Projects
None yet
Development

Successfully merging a pull request may close this issue.