简体   繁体   中英

React Native - Components not loading

Having a strange issue in React Native - I'm trying to build a list of cards, where each card represents a card with user information (eg first name, last name, etc).

The card is a separate component that takes an _id prop. The _id is then used in the Card component to fetch the data. The cards are then instantiated like this:

Card.js

import React, { useState, useEffect } from 'react';

import {
  View,
  Text,
  Image,
  StyleSheet
} from 'react-native';

const Card = (props) => {
   const [bio, setBio] = useState(true) 
   const [email, setEmail] = useState(true) 
   const [firstName, setFirstName] = useState(true) 
   const [lastName, setLastName] = useState(true) 
   const [phoneNumber, setPhoneNumber] = useState(true) 
   const [photoData, setPhotoData] = useState(true) 

   const baseUrl = "node-js-backend-url"
   const fileReaderInstance = new FileReader();
   
   loadImage = (url, headers) => {
      fetch(url, {headers})
      .then(response => response.blob())
      .then(blob => {
         fileReaderInstance.readAsDataURL(blob);
         fileReaderInstance.onload = () => {
            base64Data = fileReaderInstance.result;
            setPhotoData(base64Data);
         }
      })
      .then(() => {
         console.log("Card details: ")
         console.log(firstName)
         console.log(lastName)
      })
      .catch(error => {
         console.log("Error getting image for card: " + props._id + ", token: " + props.token+ ", "+ error)
      });
   }

   loadCardData = (url) => {
      let headers = new Headers()
      headers.append("Content-Type", "application/x-www-form-urlencoded");
      headers.append("x-auth-token", props.token)

      fetch(url, {headers})
      .then(response => response.json())
      .then(data => {
         setFirstName(data.firstName ? data.firstName : null)
         setLastName(data.lastName ? data.lastName : null)
         setBio(data.bio ? data.bio : null)
         setEmail(data.email ? data.email : null)
         setPhoneNumber(data.phoneNumber ? data.phoneNumber : null)

         var photoUrl = baseUrl + "/api/card/" + data._id + "/file/image/" + data.photo.filename;
         loadImage(photoUrl, headers)
      })
      .catch(error => {
         console.log("Error fetching card data for card: " + props._id + ", token: " + props.token + ", " + error)
         
      });
   }

   useEffect(() => {
         let url = baseUrl + "/api/card/" + props._id
         loadCardData(url)
         console.log("Successfully loaded card.")
   }, []);

  return (
    <>
         <View 
            style={cardStyles.outerView}
         >
            <View style={cardStyles.photoView}>
               <Image
                  source={{
                     uri: photoData.length > 0 ? photoData : null
                   }}
                   style={cardStyles.portrait}
               ></Image>
            </View>
            <View style={cardStyles.infoView}>
               <Text style={cardStyles.names}>{firstName} {lastName}</Text>
               <Text style={cardStyles.bio}>{bio}</Text>
               <Text style={cardStyles.contact}>{email}</Text>
               <Text style={cardStyles.contact}>{phoneNumber}</Text>
            </View>
         </View>
    </>
  );
};

const cardStyles = StyleSheet.create({
   outerView: {
      backgroundColor: '#F9F8F8',
      shadowColor: "#000",
      shadowOffset: {
         width: 0,
         height: 2,
      },
      shadowOpacity: 0.23,
      shadowRadius: 2.62,
      elevation: 4,
      borderRadius: 10,
      padding: 15,
      flexDirection: "row",
      justifyContent: "center",
      alignItems: "center"
   },
   names: {
      fontSize: 18,
      margin: 5
   },
   bio: {
      margin: 10
   },
   contact: {
      fontSize: 14
   },
   photoView: {
      flex: 0.425,
      width: 100,
      height: 100,     
      borderRadius: 250, 
      marginRight: 15,
      backgroundColor:"#FFFFFF",
      justifyContent: "center",
      alignItems: "center"
   },
   portrait: {
      flex: 1,
      width: "100%",
      height: "100%",
      resizeMode: 'contain',
      borderRadius: 100,
   },
   infoView: {
      flex: 1
   }
})

export default Card;

Connections.js

import React, { useState, useEffect } from 'react';
import { styles } from '../styles/styles'
import Card from '../components/Card'

import {
  SafeAreaView,
  View,
  ScrollView,
  Text,
  StatusBar,
  StyleSheet
} from 'react-native';

const ContactsPage: () => React$Node = () => {
  const baseUrl = "node-js-backend-url"
  const token = "[x-auth-token-for-testing]"
  
  const [connections, setConnections] = useState([]) 
  const [loading, setLoading] = useState() 

  loadConnections = () => {
    let headers = new Headers()
    headers.append("Content-Type", "application/x-www-form-urlencoded");
    headers.append("x-auth-token", token)

    let url = baseUrl + "user/connections"
    console.log("url: " + url)

    fetch(url, {headers})
    .then(response => response.json())
    .then(data => {
      setConnections(data.connections)
      setLoading(false)
    })
    .catch(error => {
        console.log("Error fetching connections: " + error)
    });
  }
  useEffect(() => {
    setConnections([])
    setLoading(true)
    loadConnections()
  }, []);

  return (
    <>
        <StatusBar barStyle="dark-content" />
        <SafeAreaView style={styles.container}>
          <View style={styles.heading}>
            <Text style={styles.headline}>Connections</Text>
          </View>
          <ScrollView style={styles.body, cardStyles.cardList}>
            {/* {loading == true && <Text>Loading...</Text>}
            {(loading == false && connections.length == 0) && <Text>You have no connections yet! Add some to get started...</Text>}
            {(loading == false && connections.length > 0) && connections.map(connection => (<Card key={connection} _id={connection} token={token}></Card>))} */}
            {/* {( loading == false  */}
              {/* <Card key="card1" _id="6005ef770aeb9b3a7035311f" token={token} style={cardStyles.card}></Card> */}
              {/* <Card key="card2" _id="6011d9d39689ab00049fc6e8" token={token} style={cardStyles.card}></Card> */}
              {/* <Card key="card3" _id="6005ef770aeb9b3a7035311f" token={token} style={cardStyles.card}></Card> */}
              <Card key="card4" _id="6011d9d39689ab00049fc6e8" token={token} style={cardStyles.card}/>
              <Card key="card5" _id="6005ef770aeb9b3a7035311f" token={token} style={cardStyles.card}/>
          </ScrollView>
        </SafeAreaView>
    </>
  );
};

const cardStyles = StyleSheet.create({
  card: {
    marginTop: 10,
    marginBottom: 10
  },
  cardList: {
    height: "80%"
 }
})

export default ContactsPage;

控制台记录卡片数据,可以看到卡片加载成功 连接页面截图

The result is that 2 cards are rendered, however only the last card contains any information. If I create only a single card, all the information is shown as expected. Adding more than two cards results in all but the last being unpopulated (ie not showing the user data).

The interesting part is that the correct data is loaded even for cards that don't show it. Logging the retrieval of each card's data shows that user data has been set in the state. Also, the margins from the card style are not applied. What could be the case of this, and how can it be fixed?

You're declaring all of your data fetching functions as global variables (eg loadCardData , loadImage , loadConnections ) because you haven't added a keyword in front of them like const to define their scope.

Change to:

const loadCardData = (url) => 

And repeat for all instances.

Unrelated note: You may want to reconsider your use of true for the default state of all of your useState hooks. You do a true/false comparison later on, which will end up returning true before the values are initialized. Consider initializing them as empty versions of the types they will become eg useState("") for a string or as undefined.

Without this, they go into the global scope and end up capturing only the last occurrences of setName , etc

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