简体   繁体   中英

react native jsx is being rendered before state is updated and throwing an undefined error

I have a react native project I am working on. There are two screens. One screen is a list of properties and details that render based on an api call to zillows api. When a property is clicked on, I want it to go to a different screen with the zpid that is passed to the new screen.

On the new screen, I have two states that are set. I have a loading state that is set to true. loading will be set to true until the new api call is made and until the loading state is set to false, I want to screen to render loading text. Once the loading is set to false and the property state is set to the results of the api call, I want to render the property details on the screen.

My issue right now is that I have a useEffect right now that runs the zillow api call right when the screen loads. Once the results are stored in the property state, I set the loading to false.

When this happens I am getting an undefined variable issue that is suppsed to render the property price from the property state. I know that the issue is caused when it is trying to grab from the property state before it is loaded but I am not sure how to delay rendering the content until after the property state is set.

component:

import React, { useState, useEffect } from 'react'
import { Dimensions } from 'react-native'
import { View, Image, Text, StyleSheet, FlatList, TextInput, Button } from 'react-native'
import { ScrollView } from 'react-native-gesture-handler'

import { propertyOptions } from '../api/zillowApi'

import { convertToDollars } from '../utilities'

import axios from 'axios';

const PropertyDetail = (props) => {
  const {
    route
  } = props

  const [property, setProperty] = useState()
  const [loading, setLoading] = useState(true)

  let zpid = route.params.zpid

  let deviceWidth = Dimensions.get('window').width
  var aspectHeight = (deviceWidth / 1.78) + 1
  let carouselImageWidth = (deviceWidth / 4)
  let carouselImageHeight = (carouselImageWidth / 1.78) 

  let options = propertyOptions
  options.params.zpid = zpid
 
  useEffect(() => {
    axios.request(options)
      .then(function (response) {
        // console.log("single property: ", response.data);
        setProperty(response)
        setLoading(false)
      }).catch(function (error) {
        console.error(error);
      });
  }, [])

  const loadingContent = <Text>Loading</Text>

  const loadedContent = <ScrollView>
    <View style={styles.imagesContainer} className="images-container">
      <View style={[styles.mainImageContainer,{height: aspectHeight}]} className="main-image-container">
        <Image style={styles.mainImage} source={{uri: property.data.imgSrc}}/>
      </View>
      <View style={styles.carouselContainer} className="image-carousel-image">
        <FlatList 
          horizontal
          data={carouselImages}
          renderItem={({item}) => <Image style={{height: carouselImageHeight, width:carouselImageWidth}} source={require('../../assets/luxury-home-1.jpeg')}/>}
        />
      </View>
    </View>
    
    ...


        <View style={styles.taxHistoryContainer}>
      <View style={styles.contactContainer}>
        <Text>Listing Agent: Omar Jandali (DRE# 02151051)</Text>
        <Text>Listing Brokerage: Compass (DRE# 00132433)</Text>
      </View>
      <View style={styles.expense}>
        <TextInput placeholder='First Name'/>
        <TextInput placeholder='Last Name'/>
      </View>
      <View style={styles.expense}>
        <TextInput placeholder='Subject'/>
      </View>
      <View style={styles.expense}>
        <TextInput placeholder='Message'/>
      </View>
      <Button title='Submit'/>
      
    </View>
    
  </ScrollView>

  return(
    <View>
      {
        loading == true ? loadingContent : loadedContent
      }
    </View>
  )
}


I want to delay rendering the content until I know that the property state is set and I dont know how to do that...

You can do an early return, below the useEffect and your components.

(sorry if the component names are different).

if (!property) return <LoadingContent />

return (
  <View>
   <LoadedContent />
  </View>
 )

Also, you should set your loading to false in the useState . Then when making your axios call, set it to true.

const [loading, setLoading] = useState(false);

useEffect(() => {
    setLoading(true);

  try {
    const res = await axios.request(options);
  
    if (res) {
     setProperty(res.data);
    }
  } catch ...
   finally {
    setLoading(false);
  }
  
}, [])

on another note, is there a reason why the initial value of your property state value is nothing? I normally would set it to null , so it's definite that it's false if there is no update to that state.

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