FlatList renderItem is called multiple times

I'm using FlatList to display data from an API call:

render() {
  const { navigation } = this.props;
  const animating = this.state.animating;

  return (
    <View style={styles.container}>
      <ActivityIndicator
        animating = {animating}
        color = '#E3A141'
        size = {120}
        style = {styles.activityIndicator}
      /> 
      <FlatList                    
        ref='listRef'
        data={this.props.data}
        renderItem={this.renderItem}
        keyExtractor={(item, index) => index.toString()}
      />   
      <SocialFooter navigation={navigation}/>
    </View>
  )
}

renderItem({item, index}) {
  console.log("renderItem(item): ", item, "index: ", index)
}

The returned data is an array with 3 objects and an array, as you can see in the output renderItem() is called once per object in the array:

renderItem(item):  Object {
12:47:38:   "antiparasitario": "SI",
12:47:38:   "color": "BLANCO",
12:47:38:   "contrato": "00345",
12:47:38:   "datoCobol": 1,
12:47:38:   "dtomedicamentos": "SI",
12:47:38:   "dtovacunas": "SI",
12:47:38:   "especie": "01",
12:47:38:   "extraccsangre": "NO",
12:47:38:   "fecnac": "2013-05-01",
12:47:38:   "hayPendientes": 0,
12:47:38:   "nombre": "ASTOR",
12:47:38:   "orden": "01",
12:47:38:   "ordenLocal": "01",
12:47:38:   "peso": "030.000",
12:47:38:   "raza": 107,
12:47:38:   "rutafoto": "",
12:47:38:   "sexo": "M",
12:47:38:   "sociode": "MASCOTA24",
12:47:38:   "traslado": "SI",
12:47:38: } index:  0
12:47:39: renderItem(item):  Object {
12:47:39:   "antiparasitario": "SI",
12:47:39:   "color": "BALNCO",
12:47:39:   "contrato": "00345",
12:47:39:   "datoCobol": 1,
12:47:39:   "dtomedicamentos": "SI",
12:47:39:   "dtovacunas": "SI",
12:47:39:   "especie": "01",
12:47:39:   "extraccsangre": "NO",
12:47:39:   "fecnac": "2017-05-01",
12:47:39:   "hayPendientes": 0,
12:47:39:   "nombre": "APACHE",
12:47:39:   "orden": "02",
12:47:39:   "ordenLocal": "02",
12:47:39:   "peso": "025.000",
12:47:39:   "raza": 107,
12:47:39:   "rutafoto": "",
12:47:39:   "sexo": "M",
12:47:39:   "sociode": "MASCOTA24",
12:47:39:   "traslado": "SI",
12:47:39: } index:  1
12:47:39: renderItem(item):  Object {
12:47:39:   "antiparasitario": "SI",
12:47:39:   "color": "MARRON",
12:47:39:   "contrato": "00345",
12:47:39:   "datoCobol": 1,
12:47:39:   "dtomedicamentos": "SI",
12:47:39:   "dtovacunas": "SI",
12:47:39:   "especie": "01",
12:47:39:   "extraccsangre": "NO",
12:47:39:   "fecnac": "2015-05-01",
12:47:39:   "hayPendientes": 0,
12:47:39:   "nombre": "CORA",
12:47:39:   "orden": "03",
12:47:39:   "ordenLocal": "03",
12:47:39:   "peso": "005.000",
12:47:39:   "raza": 43,
12:47:39:   "rutafoto": "",
12:47:39:   "sexo": "H",
12:47:39:   "sociode": "MASCOTA24",
12:47:39:   "traslado": "SI",
12:47:39: } index:  2
12:47:39: renderItem(item):  Array [
12:47:39:   Object {
12:47:39:     "dni": "0303456",
12:47:39:     "socioID": 2020,
12:47:39:     "titular": "Jonny Melaslavo",
12:47:39:   },
12:47:39: ] index:  3
12:47:39: renderItem(item):  Object {
12:47:39:   "antiparasitario": "SI",
12:47:39:   "color": "BLANCO",
12:47:39:   "contrato": "00345",
12:47:39:   "datoCobol": 1,
12:47:39:   "dtomedicamentos": "SI",
12:47:39:   "dtovacunas": "SI",
12:47:39:   "especie": "01",
12:47:39:   "extraccsangre": "NO",
12:47:39:   "fecnac": "2013-05-01",
12:47:39:   "hayPendientes": 0,
12:47:39:   "nombre": "ASTOR",
12:47:39:   "orden": "01",
12:47:39:   "ordenLocal": "01",
12:47:39:   "peso": "030.000",
12:47:39:   "raza": 107,
12:47:39:   "ruta_img": 3,
12:47:39:   "rutafoto": "",
12:47:39:   "sexo": "M",
12:47:39:   "sociode": "MASCOTA24",
12:47:39:   "traslado": "SI",
12:47:39: } index:  0
12:47:39: renderItem(item):  Object {
12:47:39:   "antiparasitario": "SI",
12:47:39:   "color": "BALNCO",
12:47:39:   "contrato": "00345",
12:47:39:   "datoCobol": 1,
12:47:39:   "dtomedicamentos": "SI",
12:47:39:   "dtovacunas": "SI",
12:47:39:   "especie": "01",
12:47:39:   "extraccsangre": "NO",
12:47:39:   "fecnac": "2017-05-01",
12:47:39:   "hayPendientes": 0,
12:47:39:   "nombre": "APACHE",
12:47:39:   "orden": "02",
12:47:39:   "ordenLocal": "02",
12:47:39:   "peso": "025.000",
12:47:39:   "raza": 107,
12:47:39:   "ruta_img": 3,
12:47:39:   "rutafoto": "",
12:47:39:   "sexo": "M",
12:47:39:   "sociode": "MASCOTA24",
12:47:39:   "traslado": "SI",
12:47:39: } index:  1
12:47:39: renderItem(item):  Object {
12:47:39:   "antiparasitario": "SI",
12:47:39:   "color": "MARRON",
12:47:39:   "contrato": "00345",
12:47:39:   "datoCobol": 1,
12:47:39:   "dtomedicamentos": "SI",
12:47:39:   "dtovacunas": "SI",
12:47:39:   "especie": "01",
12:47:39:   "extraccsangre": "NO",
12:47:39:   "fecnac": "2015-05-01",
12:47:39:   "hayPendientes": 0,
12:47:39:   "nombre": "CORA",
12:47:39:   "orden": "03",
12:47:39:   "ordenLocal": "03",
12:47:39:   "peso": "005.000",
12:47:39:   "raza": 43,
12:47:39:   "ruta_img": 3,
12:47:39:   "rutafoto": "",
12:47:39:   "sexo": "H",
12:47:39:   "sociode": "MASCOTA24",
12:47:39:   "traslado": "SI",
12:47:39: } index:  2
12:47:39: renderItem(item):  Array [
12:47:39:   Object {
12:47:39:     "dni": "0303456",
12:47:39:     "socioID": 2020,
12:47:39:     "titular": "Jonny Melaslavo",
12:47:39:   },
12:47:39: ] index:  3

This is the fetch call:

function makeDataRequest(token, nro_socio) {
  let datos_socio = [];
  fetch('http://www.api/' + nro_socio, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'token': token
      },
    }).then((response) => response.json())
    .then((responseJson) => {
      if (responseJson.errores) {
        dispatch({
          type: DATA_AVAILABLE,
          data: ['sin_serv_vet']
        });
      } else {
        let responseObj = responseJson.mascotas;
        if (responseObj == '') {
          dispatch({
            type: DATA_AVAILABLE,
            data: ['sin_mascotas']
          });
        }
        datos_socio.push({
          titular: responseJson.titular,
          dni: responseJson.doc,
          socioID: nro_socio
        });
        responseObj.push(datos_socio);
        dispatch({
          type: DATA_AVAILABLE,
          data: responseObj
        });
      }
    })
    .catch((error) => {
      console.error(error);
    });
}

I've found lots of similar posts and they say is a react native bug, what can i do?. Thanks

Edit:

class UserPage extends Component {

    static navigationOptions = { header: null }

    constructor(props) {
        super(props);
        this.state = {
            animating: true,
            isModalVisible: false
        };
        dni = props.match.params.username;           
        this.props.getData(dni);
        this.renderItem = this.renderItem.bind(this);
    }

    componentWillReceiveProps(nextProps) {             
        this.closeActivityIndicator();
        if (nextProps.data == "dni_incorrecto") {
            this.props.history.push('/'+"dni_incorrecto");
        } else if (nextProps.data == "sin_serv_vet") {
            this.props.history.push('/'+"sin_serv_vet");
        } else if (nextProps.data == "sin_mascotas") {
            this.props.history.push('/'+"sin_mascotas");
        }
    }

    closeActivityIndicator() {
        this.setState({ 
            animating: false 
        })
    }

    render() {
        return null;
        console.log("data length: ", this.props.data.length);
        if (this.props.data.length) { 
            const datosSocio = this.props.data[0].datos_socio;

            return ( 
                <View>
                    <View style={styles.header_container}>
                        <Image source={require('./img/logo-mini.png')} style={styles.logo_img} />
                        <Link to="/" style={styles.back_link} underlayColor="#f0f4f7">
                            <Image  
                                source={require("./img/back.png")}
                                style={styles.back_img}
                            />
                        </Link>
                    </View>
                    <View style={styles.container_credential}>
                        <Image source={require('./img/base-face-image.png')} style={styles.customer_img} />
                        <View style={styles.credential_details}>
                            <Text style={styles.customer_fullName}>
                                {datosSocio.titular} 
                            </Text>
                            <Text style={styles.customer_member_id}>
                                Socio ID: {datosSocio.socioID}                         
                            </Text>
                            <Text style={styles.customer_dni}>
                                DNI: {datosSocio.dni}                
                            </Text>
                            <Text style={styles.credential_notice_title}>
                                ¡IMPORTANTE! 
                            </Text>
                            <Text style={styles.credential_notice_content}>
                                El uso de esta credencial debe ser acompañado por el DNI del titular.
                            </Text>
                        </View>
                    </View>
                    <View style={styles.container_mascotas_list_title}> 
                        <Text style={styles.mascotas_list_title_text_first}>Mis</Text><Text style={styles.mascotas_list_title_text_last}> mascotas:</Text>                    
                    </View>
                    <View style={styles.mascotas_list_title_underline}></View>
                    <ScrollView>          
                        {this.props.data.map(mascota => {
                            return ( 
                            <View key={mascota.nombre}>

                                    <Text style={styles.mascotas_list_item_details_name}>Nombre: {mascota.nombre}</Text>                                    
                                    <Text style={styles.mascotas_list_item_details_name}>Raza: {getRaza(mascota.raza)}</Text>
                                    <Text style={styles.mascotas_list_item_details_name}>Especie: {getEspecie(mascota.raza)}</Text>
                                    <Text style={styles.mascotas_list_item_details_sex_hair}>Sexo: {mascota.sexo}</Text>
                                    <Text style={styles.mascotas_list_item_details_sex_hair}>Color pelo: {mascota.color}</Text>

                            </View>
                            );
                        })}
                    </ScrollView>
                </View>
          );
        } else {
          return null;
        }
      }
};

function getRaza(id_raza) {
    for (let i = 0; i < razas.length; i++) {
        if (razas[i][0] == id_raza) {
            return razas[i][1];
        }
    }
}

function getEspecie(id_raza) {
    for (let i = 0; i < razas.length; i++) {         
        if (razas[i][0] == id_raza) {
            return razas[i][2];
        }
    }
}

// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
    return {
        loading: state.dataReducer.loading,
        data: state.dataReducer.data
    }
}

// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/home.js)
function mapDispatchToProps(dispatch) {
    return bindActionCreators(Actions, dispatch);
}

//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(UserPage);

However now the problem is that the scrollview is not scrolling....

Edit 2: I resolved this by wrapping all the return() content inside ScrollView.-


Solution 1:

Normal re renders are normal in RN. That is just RN updating the react tree and updating views. If you dont have a performance issue then you can ignore this.

Otherwise, please explain the problem because the console log is fine. Render item executes for EACH item in the data array you pass to FlatList. That is the render function for each item, not the render for the whole list.

If this is not clear enough, and it is easier for you, feel free to add here a comment in Spanish and then we can update the answer (Only saying this since by your user, you seam spanish speaker as I am)