1

I'm new to React Native` and I've been trying to create a view with the number of buttons dynamically created from the data that I fetch from the API.

One of my problems is that the data that is returned doesn't have any name that I can point like item.number.

First I tried to do a <Flatlist/> but I wasn't getting the design that I wanted, like this:

so now I'm trying to make in a different way, but I'm always getting the error:

Cannot read property '0' of undefined

data.json:


{  
   "sEcho":0,
   "iTotalRecords":"75",
   "iTotalDisplayRecords":"73",
   "aaData":[  
      [  
         "1",
         "1",
         "Consumidor Final",
         "999999990",
         "",
         "",
         null,
         ", ",
         "21110000",
         "1",
         "1",
         "1"
      ],
      [  
         "2",
         "1000",
         "xxxxxx",
         "xxxxx",
         "",
         "",
         null,
         ", ",
         "xxxx",
         "15",
         "1",
         "1"
      ]
   ]
}

How I am fetching the data;

getClientes = async (token, opcao, search, pag, numRows) => {
  var url = "https://xxxx/xxxx/xxx/xxxx/xxx/tabelas/clientes?_token=" + token + "&opcao= " + opcao + "&search=&pag=&numRows=" + numRows;

  fetch(url)
    .then((response) => response.json())
    .then((responseJson) => {
      this.setState({
        dataSource: responseJson.aaData,
        isLoading: false,
        numRows: responseJson.iTotalDisplayRecords
      })

    console.log(responseJson.iTotalDisplayRecords + " total");
    console.log("CLIENTES: " + responseJson.aaData[0][2]);
    console.log(this.state.dataSource[0][2]);
  })
  .catch((error) => {
    console.log(error);
  })
}

How I render the buttons:

renderClientes = (numRows, data) => {
  console.log(numRows + "azxcvb");
  console.log(data.length + "render Clientes");
  const views = [];

  for (let i = 0; i <= numRows; i++) {
    for (let j = 0; j <= 11; j++) {
      views.push(
        <TouchableOpacity style={{ flex: 1, flexDirection: 'row', marginBottom: 3 }} key={i}>
          <View style={{ flex: 1, justifyContent: 'center', marginLeft: 5 }}>
            <Text style={{ fontSize: 18, color: 'green' }}>
              {data[i][j]}
            </Text>
          </View>
        </TouchableOpacity>
      );
    }
  }

  return views;
}

then:

 render() {
  return (
   ...
 (this.state.numRows != 0) ? this.renderClientes(this.state.numRows, this.state.dataSource) : null)
}

How can I solve my problem? And if there is any easier way to do this, what is?

3 Answers 3

1

First of all a recommendation for your API:

let url = "https://clientes?_token=" + token + "&opcao= ";

Never send tokens on the URL because it's pretty easy to hack them. You can send hidden data on your headers.


As @Austin Greco said numRows is '73', but your array only has 2 sets of rows. I recommend sending numRows as an integer instead of a string.

Also for building complex interfaces from data, you should use objects that help you describe better your data, instead of an array of arrays (aaData) as you have. Taking the image you show us:

You can have an object like:

{
  gt: 'GT 2019/01',
  name: 'Luis Filipe Gomes Rodrigues',
  total: '179,90 €',
  open: true,
  nif: 9999999999,
  date: '01/01/2019',
}

instead of having

arr = ['GT 2019/01', 'Luis Filipe Gomes Rodrigues', '179,90 €', true, 9999999999, '01/01/2019']

name = arr[1]

So I took some time to create an snack for you to follow up: @abranhe/clients-view it looks something like this:

Wrap the code:

import React, { Component } from 'react';
import {
  View,
  Text,
  Image,
  StyleSheet,
  TouchableOpacity,
  Dimensions,
  ScrollView,
} from 'react-native';
import { AntDesign } from '@expo/vector-icons';

import data from './data.js';

export default class StackOverflowAnswer extends Component {
  constructor(props) {
    super(props);

    this.state = {
      data: data,
      isLoading: false,
    };
  }

  renderClientLeft(client) {
    return (
      <View style={styles.clientLeft}>
        <Text style={styles.gt}>GT {client.gt}</Text>
        <Text style={styles.name} numberOfLines={1}>
          {client.name}
        </Text>
        <Text style={styles.nifAndDate}>NIF: {client.nif}</Text>
        <Text style={styles.nifAndDate}>Data: {client.date}</Text>
      </View>
    );
  }

  renderClientRight(client) {
    let hoursColor = client.open ? '#588f40' : '#4973a3';
    let hoursText = client.open ? 'Aberto' : 'Fechado';

    return (
      <View style={styles.clientRight}>
        <View style={{ justifyContent: 'space-around' }}>
          <View style={styles.total}>
            <Text style={styles.name}>Total:</Text>
            <Text style={styles.totalVal}>{client.total}</Text>
          </View>
          <View style={[{ backgroundColor: hoursColor }, styles.hours]}>
            <Text style={styles.hoursText}>{hoursText}</Text>
          </View>
        </View>

        <View style={styles.arrowContainer}>
          <AntDesign name="right" color="#bf5e2a" />
        </View>
      </View>
    );
  }

  renderClient(client) {
    return (
      <View style={styles.clientContainer}>
        <View style={styles.client}>
          {this.renderClientLeft(client)}
          {this.renderClientRight(client)}
        </View>
      </View>
    );
  }

  renderClientes = clients => {
    return clients.map((client, i) => {
      return (
        <View key={i} style={styles.clientsSeparator}>
          {this.renderClient(client)}
        </View>
      );
    });
  };

  render() {
    const { data } = this.state;
    return (
      <ScrollView style={styles.container}>
        {this.renderClientes(data)}
      </ScrollView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#ededed',
    marginTop: 20,
    alignItems: 'center',
  },
  clientContainer: {
    width: Dimensions.get('window').width * 0.95,
    borderWidth: 1,
    borderColor: '#000000',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.4,
    shadowRadius: 1,
    backgroundColor: '#ffffff',
  },
  client: {
    flexDirection: 'row',
    borderRadius: 1,
    margin: 4,
  },
  clientLeft: {
    width: '60%',
    marginVertical: 5,
  },
  clientRight: {
    justifyContent: 'space-between',
    flexDirection: 'row',
    height: '100%',
    width: '40%',
  },
  gt: {
    fontWeight: '700',
    color: '#bf5e2a',
    margin: 2,
  },
  name: {
    fontWeight: '700',
    margin: 2,
  },
  nifAndDate: {
    color: '#8a8a8a',
    margin: 2,
  },
  arrowContainer: {
    justifyContent: 'center',
    alignItems: 'center',
  },
  totalVal: {
    color: '#bf5e2a',
    marginLeft: 10,
  },
  total: {
    flexDirection: 'row',
  },
  hours: {
    width: '70%',
    justifyContent: 'center',
    alignItems: 'center',
  },
  hoursText: {
    color: '#ffffff',
    margin: 5,
  },
  clientsSeparator: {
    marginBottom: 5,
  },
});
Sign up to request clarification or add additional context in comments.

Comments

1

It looks like your numRows is 73, but your array only has 2 sets of rows in it?

Usually it's better to .map through an array, so it will automatically go through each item. Something like this:

renderClientes = (numRows, data) => {
  console.log(numRows + "azxcvb");
  console.log(data.length + "render Clientes");

  return data.map((row, i) =>
    row.map((item, j) => (
      <TouchableOpacity style={{ flex: 1, flexDirection: 'row', marginBottom: 3 }} key={`${i},${j}`}>
          <View style={{ flex: 1, justifyContent: 'center', marginLeft: 5 }}>
              <Text style={{ fontSize: 18, color: 'green' }}>
                  {item}
              </Text>
          </View>
      </TouchableOpacity>
    ))
  );
}

Comments

1

Your getting this error

error: Cannot read property '0' of undefined, cause you are looping throught a n number array n+1 times.

To Fix this just assign your setState like this:

this.setState({
            dataSource: responseJson.aaData,
            isLoading: false,
            numRows: responseJson.iTotalDisplayRecords - 1
        })

And as Austin Greco already pointed out, i would go for map also, trust me, it will make everything easier when working with arrays in React, and in JavaScript.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.