1

I have an app which displays multiple images which I load from the API. Now the problem is some of the images are expired which is causing a problem on Android, in Android the screen starts lagging as soon as the expired image loads on the screen.

I have tried replacing the image source with onError={() => this.imgRefs[img_unique_id].setNativeProps({src: [{uri: this.state.workingUri}]})} this method but its not working. I cannot use the local state as it is not saving the output in the local state.

I have tried the following code

  <Image  
      source={image.url} 
      progressiveRenderingEnabled={true}
      ref={item.id} 
      onError={(e) => this.refs[item.id].setNativeProps({source: [{uri: "working image URL"}]})} 
      resizeMethod={"scale"}>
   </Image>      

The above code gives me an undefined setNativeProps error, and if I do not use the onError on android it shows me memory leak error.

Paras Korat
  • 2,011
  • 2
  • 18
  • 36
  • Why can't you use the local state in this case? – gbalduzzi Apr 17 '19 at 08:04
  • just use `native-base` instead of raw react-native constructs, `native-base` `Image` has support for failed images by providing you prop `fallbackSource` or even `fallbackComponent` – Humoyun Ahmad Nov 28 '21 at 13:10

2 Answers2

4

Here is a complete example of that. To have own state for every FlatList item, I created a class.

import React, { Component, PureComponent } from 'react';
import { FlatList, Text, View, Image } from 'react-native';

class ItemClass extends PureComponent {
  state = {
    isImageExit: null
  }
  componentWillMount = () => {
    const { task } = this.props;
    Image.getSize(task.url, (width, height) => {
        if(width>0){
            this.setState({ isImageExit: true });
        }
        else{
            this.setState({ isImageExit: false });
        }
    }, () => {
      this.setState({ isImageExit: false });
    });
  }

  render() {
    const { isImageExit } = this.state;
    const { task } = this.props;
    const url = isImageExit ? task.url : 'https://dummyimage.com/600x400/000/fff';
    if (isImageExit === null) {
      return null;
    }
    return (
      <Image
        style={{ height: 200, width: 200 }}
        source={{ uri: url }}
      />
    );
  }
}

export default class App extends Component {
  render() {
    const data = [
      { url: 'url' },
      { url:'https://cdn.pixabay.com/photo/2017/08/05/18/53/mountain-2585069_1280.jpg' },
    ];
    return (
      <View style={{alignItems: 'center', top: 50}}>
          <FlatList
            data={data}
            renderItem={({ item }) => <ItemClass task={item} />}
          />
      </View>
    );
  }
}
akcoban
  • 953
  • 7
  • 14
  • your code worked perfectly, still the problem is same as soon as the error image comes into the view the app becomes slow. – Tushar Cheulkar Apr 17 '19 at 12:39
  • Instead of waiting for the image to go into error state how can I use getSize for preload the image ? – Tushar Cheulkar Apr 17 '19 at 12:41
  • Updated, maybe that's better? Firstly image.getSize will try to download and get the size of the image. While this working, nothing will render or you can render loading component or what you want. When getSize is finished, image will render too. – akcoban Apr 17 '19 at 12:47
  • I implemented the change it is still slow it think the images are making the FlatList slow after load more. I tried without the images it works fine. – Tushar Cheulkar Apr 17 '19 at 15:40
0

I think you should use data received from Api and set state accordingly inside componentWillReceiveProps because I think setting state is the best way to achieve this result. .

 this.setState = { image: { uri: 'image_uri_from_api' } }

Inside <Image> add -

 <Image style={ your_custom_style }
       source={ this.state.image }
       onError={ this.onError.bind(this) }
 />

Inside onError add this -

onError(error){
   this.setState({ image: require('your_local_image.path')})
 }

Hope it works now !