cold programmmer
cold programmmer

Reputation: 289

React Native TypeError: Cannot read property 'name' of undefined

I'm trying to fetch a list of departments from an URL in a React Native application

export default function App() {
var [department,setDepartment]=useState([])
const token = /* my token here */

  const getDepartments=()=>{  
    const url = /*my api's url here*/

      return fetch(url, {
          method: 'GET',
          headers: { "Authorization": "Bearer" + token ,
          'Accept': 'application/json',
          'Content-Type':'application/json'
      }
      })
          .then(response => response.json())
          .catch(error => console.error(error))
  }

   const getdepartment = async () => {
        await getDepartments().then((res) => {
        console.log('res',res)
          res.map((p, key) => {
            setDepartment([...department,department.push({
              name: p.name,
              id: p.id,
            })])
          });
        });
        console.log(department[0].name) //displays the correct value
      };



           
    
    return (
  <View>
<Button
  onPress={()=>getdepartment()}
  title="Learn More"
  color="#841584"
  accessibilityLabel="Learn more about this purple button"
/>
<Text>{department[0].name}</Text> //here lays the problem
              </View>
  )
}

here department[0] in JSX is undefined despite the getdepartment() function returning correct department[0]

Upvotes: 0

Views: 1745

Answers (5)

Ruchi Tiwari
Ruchi Tiwari

Reputation: 261

First the UI gets rendered and at that moment the department is empty and trying to fetch the name throws error. You can have the check as below

{department!=null && department.length>0 && department[0].name!=null &&
 <Text>{department[0].name}</Text> 
}

Upvotes: 0

WebbH
WebbH

Reputation: 2422

You should change

res.map((p, key) => {
  setDepartment([...department,department.push({
    name: p.name,
    id: p.id,
  })])

to

setDepartment(res.map((p,key)=>({name:p.name, id:p.id})))

Upvotes: 0

Kelvin Schoofs
Kelvin Schoofs

Reputation: 8718

Your problem is that your department state variable will be an empty array until your asynchronous call has succeeded and replaced it with actual data.

You should display some kind of loading indicator until the data is available:

return <View>
    <Button
        onPress= {() => getdepartment()}
        title = "Learn More"
        color = "#841584"
        accessibilityLabel = "Learn more about this purple button"
    />
    {department.length
        ? department.map(d => <Text key={d.id}>{d.name}</Text>)
        : <Text>Loading departments...</Text>}
</View>;

I've also made it display a <Text> tag for every department once it does have the data.

The second problem is your array initialization:

setDepartment([...department, department.push({
    name: p.name,
    id: p.id,
})])

You're adding the result of department.push to your array, which is a number. You want to add the object directly:

setDepartment([...department, {
    name: p.name,
    id: p.id,
}])

Upvotes: 0

driconmax
driconmax

Reputation: 942

getdepartment its called after the first render happens. So before you press the button "Learn More" department is an empty array

Try this:

<Text>{(department.length > 0)? department[0].name : ""}</Text>

Upvotes: 1

Nothingbutageek
Nothingbutageek

Reputation: 417

I think there is some misuse of states and arrays in your case, please check the usages of arrays with states and how to update them.

const [theArray, setTheArray] = useState(initialArray);


setTheArray([...theArray, newElement]);

Upvotes: 1

Related Questions