I am a newbie on React Native. I am making an application that takes data from API and displays it on the screen. When this screen is rendered gives the following error: Render Error undefined is not an object (evaluating 'character.origin.name')
Here codes at screen:
const CharacterDetailScreen = (props) => {
const { styles, colors } = useThemedValues(getStyles);
const loc = useLocalization();
**// state for character detail**
const [character, setCharacter] = useState([])
**// this id come from previous screen**
const { characterId } = props.route.params
useEffect(()=>{
Axios.get('character/'+characterId)
.then(response =>{
characterDetail = response.data
setCharacter(characterDetail)
console.log(characterDetail.name) // if characterId=89, writes "dale" in console
})
.catch(error => {
console.log(error)
})
},[])
return (
<View style={styles.container}>
<View style={styles.imageContainer}>
<Image source={{ uri: character.image }}
style={styles.image}
/>
</View>
<View style={styles.characterNameContainer}>
<Text style={styles.characterName}>{character.name}</Text>
</View>
<View style={styles.detailContainer}>
<Text style={styles.detailText} >{loc.t(texts.status)}{character.status}</Text>
<Text style={styles.detailText} >{loc.t(texts.species)}{character.species}</Text>
<Text style={styles.detailText} >{loc.t(texts.gender)}{character.gender}</Text>
<Text style={styles.detailText} >{loc.t(texts.type)}{character.type}</Text>
<Text style={styles.detailText} >{loc.t(texts.origin)}{character.origin.name}</Text>
<Text style={styles.detailText} >{loc.t(texts.location)}{character.location.name}</Text>
</View>
</View>
);
};
export default CharacterDetailScreen;
I checked the incoming data from API. Is the error due to the fact that the data has not yet come from the api when the page is rendered?
Here incoming data example from API:
const character = {
"created": "2017-12-01T10:32:08.340Z",
"episode": ["https://rickandmortyapi.com/api/episode/5"],
"gender": "Male",
"id": 89,
"image": "https://rickandmortyapi.com/api/character/avatar/89.jpeg",
"location": {"name": "Giant's Town", "url": "https://rickandmortyapi.com/api/location/14"},
"name": "Dale",
"origin": {"name": "Giant's Town", "url": "https://rickandmortyapi.com/api/location/14"},
"species": "Mythological Creature",
"status": "Dead",
"type": "Giant",
"url": "https://rickandmortyapi.com/api/character/89"
}
The simple way to fix this is to pre define your data:
const [character, setCharacter] = useState({
"created": "",
"episode": [],
"gender": "",
"id": 0,
"image": undefined,
"location": {"name": "", "url": ""},
"name": "",
"origin": {"name": "", "url": ""},
"species": "",
"status": "",
"type": "",
"url": ""
})
That way you never have to second guess whether a property on character is defined and you dont go through the head ache of adding a null check for each parameter on character you might use.
Another way to address this is to default character to null and render the ui conditionally
const CharacterDetailScreen = (props) => {
const { styles, colors } = useThemedValues(getStyles);
const loc = useLocalization();
**// state for character detail**
const [character, setCharacter] = useState(null])
**// this id come from previous screen**
const { characterId } = props.route.params
useEffect(()=>{
Axios.get('character/'+characterId)
.then(response =>{
characterDetail = response.data
setCharacter(characterDetail)
console.log(characterDetail.name) // if characterId=89, writes "dale" in console
})
.catch(error => {
console.log(error)
})
},[])
return (
<View style={styles.container}>
{character ? (
<View style={styles.imageContainer}>
<Image source={{ uri: character.image }} style={styles.image}/>
</View>
<View style={styles.characterNameContainer}>
<Text style={styles.characterName}>{character.name}</Text>
</View>
<View style={styles.detailContainer}>
<Text style={styles.detailText} >{loc.t(texts.status)}{character.status}</Text>
<Text style={styles.detailText} >{loc.t(texts.species)}{character.species}</Text>
<Text style={styles.detailText} >{loc.t(texts.gender)}{character.gender}</Text>
<Text style={styles.detailText} >{loc.t(texts.type)}{character.type}</Text>
<Text style={styles.detailText} >{loc.t(texts.origin)}{character.origin.name}</Text>
<Text style={styles.detailText} >{loc.t(texts.location)}{character.location.name}</Text>
</View>
) : (
<Loading/>
)}
</View>
);
};
export default CharacterDetailScreen;