Accessing an array that has an object as a value - reactjs

I'm using the pokeapi to with react. I want to display the values in the types property but can't seem to do so. I get a cannot read property of value 0 undefined. I've tried several methods such as storing the types object in a variable then looping through the elements but I get the same error.
My code is below:
App.js
state = {
pokemonData: []
}
componentDidMount() {
fetch(`https://pokeapi.co/api/v2/pokemon/mewtwo`)
.then(response => response.json())
.then(data => {
//console.log(data);
this.setState({pokemonData: data});
})
.catch(error => console.log(`Error fetching and parsing data ${error}`));
}
render() {
return (
<>
<Display data={this.state.pokemonData}/>
</>
);
}
Display.js
import React from 'react';
const Display = (props) => {
return (
<>
<h1 className="pokemon-name">{props.data.name}</h1>
<p>{props.data.id}</p>
<p>{props.data.types[0].type.name}</p>
</>
);
}
export default Display;

First of all you initialize your state with an empty array but the data returned by your API is actually an object. Secondly you're trying to render some data by giving an empty array as props (the actual data will come later). So props.data.types doesn't exist and props.data.types[0] will return an error.
Initialize with an empty object
state = {
pokemonData: {}
}
Then in your render function, make sure you have something in your pokemonData before rendering:
return (
<>
{this.state.pokemonData.name !== undefined &&
<Display data={this.state.pokemonData}/>
}
</>
);

Related

Accessing a property that has an object as it's value

I'm working with an API that retrieves data from a request from a user. The data returned looks something like
{
name: "mewtwo"
id: 150
types: Array(1)
0:
slot: 1
type: {
name: "psychic", url: "https://pokeapi.co/api/v2/type/14"
}
}
I want to access the "psychic" value in type but I receive an undefined for "type". I'm able to display the name property and id just fine I just can't get the "psychic" value.
const Display = (props) => {
console.log(props.data.types);
return (
<>
<h1 className="pokemon-name">{props.data.name}</h1>
<p>{props.data.id}</p>
<p>{props.data.types.type.name}</p>//Will throw an error here
</>
);
}
You are trying to access an array element. Change it to the following
props.data.types[0].type.name
const Display = (props) => {
let { data } = props;
return (
<>
<h1 className="pokemon-name">{data.name}</h1>
<p>{data.id}</p>
<p>{data.types[0].type.name}</p>
</>
);
}
Since data.types is an Array of Objects, I think you want to access the first entry in the data.types array.
So I'd replace {props.data.types.type.name} with {props.data.types[0].type.name}.
For safety reasons I'd check for the existence of that array and extract the data out of it before using it like so:
const Display = ({data}) => {
// destructure properties out of data prop
const { name, id, types } = data;
// extract type name
const typeName = types[0].name;
return (
<>
<h1 className="pokemon-name">{name}</h1>
<p>{id}</p>
<p>{typeName}</p>
</>
);
}
Taking in account that your data is coming from an API, the types prop may not be populated when you're trying to access it. We can account for that scenario like so:
Here's a wrapper component example. this component gets the async data and renders the Display component.
// import as you would to make React work.
class Wrapper extends Component {
constructor(props) {
super(props);
this.state = {
dataFromApi: false
}
}
componentDidMount() {
// async function to get data from api (ex: fetch)
fetchDataFromAPI().then(res => res.json()).then(data => {
this.setState({ dataFromApi: data })
});
}
render() {
const { dataFromApi } = this.state;
return dataFromApi ?
(<Display data={dataFromApi}>) :
(<span>loading</span>);
}
}
Hopefully that makes sense 😅.
Cheers🍻!

Cannot read property of data null in ReactJs when fetching from JSON

I am new to react andtrying to fetch JSON data in React JS but getting this error:
TypeError: Cannot read property 'data' of null
My code is :
import React from 'react';
export default class FetchJson extends React.Component {
componentDidMount()
{
fetch('https://api.myjson.com/bins/9i63i')
.then((response) => response.json())
.then((findresponse) =>{
this.setState({ data: findresponse })
//console.log(this.state.data);
//console.log(findresponse.DesignName);
})
}
render() {
return(
<ul>
{this.state.data.map((x,i) => <li key={i}>{x.DesignName}</li>)}
</ul>
);
}
}
You can see the json data here: http://myjson.com/9i63i
I want to retrieve value for key DesignName which is part1 which is not happening.
See the commented lines: both gives me the value. But when i try to access it inside return method inside render. I get error : TypeError: Cannot read property 'data' of null in this line:
{this.state.data.map((x,i) => <li key={i}>{x.DesignName}</li>)}
How to solve this?
DesignName is not an array in the response.
You can define your state like this:
state = {
data: null
}
And display the DesignName using inline if with logical && operator to solve null problem.
render() {
return (
<div>
DesignName: { this.state.data && this.state.data.DesignName}
</div>
);
}
Codesandbox
You can use an isLoading flag while waiting for your api call to finish.
state = {
data: null,
isLoading:true
}
render() {
if(this.state.isLoading) {
return(<div>loading</div>);
}
return(
<ul>
{this.state.data.map((x,i) => <li key={i}>{x.DesignName}</li>)}
</ul>
);
when your api call has finished, you can update the state like this:
this.setState({ data: findresponse, isLoading:false })

API Call Returns Data But Not Render

My console.log shows axios call returns data [object object] but it shows undefined when I try to render the data. Any ideas?
```
class CourseDetail extends Component {
state={
ID: this.props.match.params.ID,
course:[]};
componentDidMount(){
this.runSearch();
}
runSearch=async()=>{
const response= await axios.get('API\?{this.props.match.params.ID}')
this.setState({course: response.data});
//console.log shows course=[object object]
console.log("course="+response.data);
}
render(){
//course is undefined below
const course= this.state.course.map(item=> <div>(item.SUBJECT)</div>)
return (
<div>
{course}
</div>
); }
};
export default CourseDetail;
As #HolyMoly mentioned in the comments, you may need to stringify the response as JSON as well before logging it depending on what the API is returning. There's also a syntax error in your map function, you are using parentheses instead of curly braces to render the value. Depending on the structure of your data response, something like this may work:
class CourseDetail extends Component {
state={
ID: this.props.match.params.ID,
course:[]};
componentDidMount() {
axios.get('API\?{this.props.match.params.ID}')
.then(res => res.json())
.then(res => {
console.log("course="+res.data);
this.setState({course: res.data})
})
}
render(){
//course is undefined below
const course= this.state.course.map(item=> <div>{item.SUBJECT}</div>)
return (
<div>
{course}
</div>
); }
};
export default CourseDetail;
This depends on whether or not the data you are mapping is an object or an array of course. If it is in fact an object, you can map over the keys instead:
const course= Object.keys(this.state.course).map(item=> <div>{item.SUBJECT}</div>)

Accessing nested json in React

I can successfully fetch json object from the api. However there seems to be a problem in updating the state.
After printing the object I get expected and desired result console.log(videos2.list[0]); gives 1st item from the list attribute of json object, you can check how the api looks under this link:
https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars
However when updating state with setState property selectedVideo: videos.list[0] is undefined.
The code for the component:
class App extends Component {
constructor(props) {
super(props)
this.state = {
videos2:[],
selectedVideo:null
}
this.DMSearch()
}
DMSearch(){
fetch(`https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars`)
.then(result => result.json())
.then(videos2 => {
console.log(videos2.list[0]);
this.setState({
videos2: videos2.list,
selectedVideo: videos2.list[0]
});
console.log(selectedVideo);
});
}
render () {
const DMSearch = this.DMSearch()
return (
<div>
<SearchBar onSearchTermChange= {DMSearch}/>
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo=>this.setState({selectedVideo})}
videos2={this.state.videos2}/>
</div>
)
}
}
And exact error is Uncaught (in promise) ReferenceError: selectedVideo is not defined
videos2:list[0] produces correct results, but when assigned to selectedVideo it is undefined
As requested I am also including the code for vide_list which uses the objects form parent component which might be producing error here:
const VideoList = (props) => {
const videoItems = props.videos.map((video)=>{
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.etag}
video={video} />
)
})
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
)
}
To be specific, this line
const videoItems = props.videos.map((video)=> {
causes the error when reading props. I believe this has something to do with selectedVideo being null...
There is an issue at this line
console.log(selectedVideo);
You printed out an undefined variable. It should be this.state.selectedVideo.
The error selectedVideo is not defined happens because selectedVideo is not a variable, it is a key on the javascript object passed to this.setState function.
//...
this.setState({
videos2: videos2.list,
selectedVideo: videos2.list[0]
});
//...
You can mitigate the error by creating a variable:
DMSearch(){
fetch(`https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars`)
.then(result => result.json())
.then(videos2 => {
console.log(videos2.list[0]);
const selectedVideo = videos2.list[0]; // create variable
this.setState({
videos2: videos2.list,
selectedVideo: selectedVideo
});
console.log(selectedVideo);
});
}

React Redux - element not rendering on page but React not throwing error

I seem to be having an issue with my react-redux app. I'm currently using next.js, which tends to act a little weird when working with redux so i'm not sure if that's the issue. That said, I'm trying to render a component that loops through an array of objects. very simple. my mapState function is working and when I set info to state.aboutMe[0] i received the first value. Once I remove this and try to iterate through the array, Initially, I got an error that says "A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object." but I was able to resolve that by wrapping my my info.map in a <div> el.
I checked out the other questions and refactored my function in a class that extends the React.Component class but still no luck with iterating. In that case, it just prints nothing to the screen. I've added that code at the bottom as well. Please let me know if I can clear anything up. Thanks in advance!
// This code IS working
// Needed to wrap inner return statement in JSX
const heading = ({info}) => {
console.log(info);
return (
<div>{
info.map(x => {
return (
<div>
<h2>{x.title}</h2>
</div>
)
})
}
</div>
)
}
// Same code without inner return
const heading = ({info}) => {
console.log(info);
return (
<div>
{
info.map(x => (
<div>
<h2>{x.title}</h2>
</div>
)
)
}
</div>
)
}
// prints info in console
const heading = ({info}) => {
console.log(info);
return (
<div>{
info.map(x => {
<div>
<h2>{x.title}</h2>
</div>
})
}
</div>
)
}
const mapState = state => ({ info: state.aboutMe })
const Heading = connect(mapState)(heading);
// EXAMPLE WITH CLASS
// Prints nothing to the screen but doesnt throw error
class homePage extends React.Component {
render() {
const { info } = this.props;
console.log(info);
return (
<div> {
info.map(x => {
<div>
<h2>{x.title}</h2><p>{x.text}</p>
</div>
})
}
</div>
)
}
}
const mapState = state => ({ info: state.aboutMe })
const Heading = connect(mapState)(homePage);
should be
return (
<div>
{info.map(x => (<div>
<h2>{x.title}</h2><p>{x.text}</p>
</div>)
)}
</div>
)
because the div inside the map isn't really being returned
Try explicitly using a return inside map.

Resources