简体   繁体   中英

React native make “buttons” dynamically from API

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?

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>
    ))
  );
}

Your getting this error

error: Cannot read property '0' of undefined, cause you are looping throught an 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.

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,
  },
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM