Getting undefined data type error while fetching data from JSON
I have searched at many places but didn't get the suitable answer
import SavedData from "./SavedData";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true,
datas: [],
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
const todoData = response.json().then((res) => {
this.setState({ datas: res });
});
}
render() {
console.log(this.state.datas[0].description); //not able to get data
return (
<div>
{/* {this.state.datas.map((items) => (
<SavedData
key={items.countTodo}
title={items.title}
desc={items.desc}
/>
))} */}
</div>
);
}
}
Someone help me so that I can proceed
Just like Dave Newton has pointed out in the comments, the render is triggered before the request completes. This is normal and you just need to handle it properly.
If you see the console logs of this codesandbox, you can see that initially this.state.datas is just an empty array [] - so any attempt to access this.state.datas[0].description will be undefined. Only after the state is updated when the request completes, the logs show the data retrieved - this is because according to the mount lifecycle of a React Component, the render() is called before the componentDidMount() and also the request being async.
This is very common and it is even recommended by the official React docs to make HTTP calls in componentDidMount(). The docs also has provided an example to handle this issue.
import SavedData from "./SavedData";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true, // we initially set this to true
datas: [],
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
const todoData = response.json().then((res) => {
this.setState({
datas: res,
loading: false // when the request is complete, we set this to false
});
});
}
render() {
if (this.state.loading) {
// during the first render, loading will be true and we
// can return a loading message or a spinner
return (
<div>Loading...</div>
);
}
// when render is called after the state update, loading will be false
// and this.state.datas will have the fetched data
console.log(this.state.datas[0].description);
return (
<div>
{this.state.datas.map((items) => (
<SavedData
key={items.countTodo}
title={items.title}
desc={items.desc}
/>
))}
</div>
);
}
}
Your datas state is initially an empty array until your componentDidMount fires and sets the state. As a result, your console log will then be undefined until the state is set. In order to combat this you must wait for this.state.datas[0] to be true before accessing the first objects description within the array. The following code seems to work as expected
import React from "react";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true,
datas: []
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
response.json().then((res) => {
this.setState({ datas: res });
});
}
render() {
console.log(this.state.datas[0] && this.state.datas[0].description);
return (
<div>
{this.state.datas.map((items, i) => (
<div key={i}>
<div> title={items.title}</div>
<div> desc={items.description}</div>
</div>
))}
</div>
);
}
}
Related
I have this code which works perfectly:
componentDidMount() {
fetch('https://services2.arcgis.com/sJvSsHKKEOKRemAr/arcgis/rest/services/Bigfoot%20Locations/FeatureServer/0/query?where=1%3D1&outFields=*&outSR=4326&f=json')
.then((response) => {
return response.json();
})
.then((myJson) => {
this.setState({data: myJson.features[0].attributes.STATE_NAME})
console.log(this.state.data)
});
}
render() {
return (
<div className = ''>
{this.state.data}
</div>
)
}
}
However when I try to make the data set in state more general so that I can render whatever I want like this:
componentDidMount() {
fetch('https://services2.arcgis.com/sJvSsHKKEOKRemAr/arcgis/rest/services/Bigfoot%20Locations/FeatureServer/0/query?where=1%3D1&outFields=*&outSR=4326&f=json')
.then((response) => {
return response.json();
})
.then((myJson) => {
this.setState({data: myJson.features})
console.log(this.state.data)
});
}
render() {
return (
<div className = ''>
{this.state.data[0].attributes.STATE_NAME}
</div>
)
}
}
I get "Cannot read property STATE_NAME of undefined. The only change is that I tried to access the object in the render method instead of ComponentDidMount. What's the issue here?
In your component, the render() function is being called before the data is populated, even though componentDidMount() will run before the first render.
What you need is to store an intermediate loading state in your react state to indicate that the data has not yet arrived.
class RENAME_ME extends Component {
state = {
loaded: false,
data: [],
};
componentDidMount() {
fetch(
"https://services2.arcgis.com/sJvSsHKKEOKRemAr/arcgis/rest/services/Bigfoot%20Locations/FeatureServer/0/query?where=1%3D1&outFields=*&outSR=4326&f=json"
)
.then((response) => {
return response.json();
})
.then((myJson) => {
this.setState({
data: myJson.features[0].attributes.STATE_NAME,
loaded: true,
});
console.log(this.state.data);
});
}
render() {
// Data is still loading, display an intermediate message
if (!this.state.loaded) {
return <p>Loading...</p>;
}
return <div className="">{this.state.data}</div>;
}
}
You shouldn't read from the state until it's present:
render() {
return (
<div className = ''>
{(this.state.data && this.state.data.length) ? this.state.data[0].attributes.STATE_NAME : `still loading, or maybe an error`}
</div>
)
}
Only display the state when it is present so this condition has 2 parts.
First part(this.state.data) is only true when the data is saved in the state so the next part(this.state.data[0].attributes.STATE_NAME) runs after that
render() {
return (
<div className = ''>
{this.state.data && this.state.data[0].attributes.STATE_NAME}
</div>
)
}
}
Your state 'data' is not properly initialized to handle object maybe
are they initialized like this?
this.state = {
data: []
You can render the value whenever it is present by
{this.state.data[0].attributes && this.state.data[0].attributes.STATE_NAME}
This is the api http://skunkworks.ignitesol.com:8000/books/ ,
I am trying to fetch the array results from it using the fetch method but instead get an error cannot fetch value of undefined
import React, { Component } from 'react';
class App extends Component {
constructor() {
super()
this.state = {
books: []
}
}
componentDidMount() {
fetch('http://skunkworks.ignitesol.com:8000/books/')
.then(res => res.json())
.then(data => this.setState({ books: data }))
.catch(e => {
console.log(e);
return e;
});
}
render() {
let book = []
book = this.state.books.results;
console.log(book[0])
return (
<div>
<h1>Books</h1>
</div>
)
}
}
export default App;
this is my code.
Also I have observed that json data are usually like [{}] but here it is {} format.
please suggest me some solution.....
As I see from your url link, you json array of data is present in the results key of the returned object from the API.
So if you're only interested by the results you should do something like that :
import React, { Component } from 'react';
class App extends Component {
state = {
books: []
}
async componentDidMount() {
const objectFromUrl = await fetch('http://skunkworks.ignitesol.com:8000/books/')
const data = await objectFromUrl.json() //first way
// or you can use destructuring way
const { results } = await objectFromUrl.json() //second way
this.setState({
books: data.results // results key contains your '[{}]' data an array of objects
})
}
render() {
const { books } = this.state
return (
<div>
<h1>Books</h1>
{books.map(book => (
<h2> {book.id} </h2>
)}
</div>
);
}
// You can use destructuring again to get only key you're interested by
render() {
const { books } = this.state
return (
<div>
<h1>Books</h1>
{books.map(({id, formats}) => (
<h2> {id} </h2>
<h2> { formats[ˋapplication/pdf’] } </h2>
)}
</div>
);
}
}
I created a class to get api.
export default class ProductDetail extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
product : []
}
}
componentDidMount() {
this.getProductRequest();
}
...
then create getProductRequest function:
async getProductRequest() {
let response = await fetch('https: ...
let json = await response.json();
console.log(json);
this.setState({ product : json.data});
}
the console result is:
{id: 225782, title: "test", images: Array(1), price: "1$"}
Now in render i get same result:
render() {
console.log(this.state.product);
return (...
Now I try to read params:
render() {
console.log(this.state.product.title);
return (...
But I get this error:
TypeError: Cannot read property 'title' of underfined
what's the wrong?
Edit: Structure:
export default class ProductDetail extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
product : []
}
}
componentDidMount() {
this.getProductRequest();
}
render() {
console.log(this.state.product.title);
return (
<View> <Text style={styles.name}>title</Text></View>
);
}
async getProductRequest() {
try {
let id = this.props.navigation.state.params.productId;
let response = await
fetch('https://www.example.com/product', {
method : 'POST',
headers : {
'Accept' : 'application/json',
'Content-Type' : 'application/json'
},
body : JSON.stringify({
id
})
});
let json = await response.json();
//json: {"data":{id: 225782, title: "test", images: Array(1), price: "1$"},"status":"success"}
this.setState({ product : json.data});
} catch(error) {
//console.log(error)
}
}
}
...
Because, componentDidMount() re-render after the first execution of rendering. So, when you are putting console.log(this.state.product.title); in the render before return, it doesn't get the title param first time.
After the re-render, the value will be available. So, if you want to check the output put console.log elsewhere or just remove it
Edit
You can call this.getProductRequest(); in componentWillMount() instead of componentDidMount()
componentWillMount() {
this.getProductRequest();
}
let product = JSON.parse(this.state.product
if(product.title){
console.log(product.title)
}
Let with above code. If you are getting string in your state, it may create an issue. Let me know if its work.
As said react official documentation :
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree)
it does mean that first time your render method is unable to read the title of your product (first time that your render method is invoked, this.state.product is still an empty array). I suggest you to check if your array is empty
render() {
if (this.state.product) {
return (
<Text>Loading...</Text>
} else
return (
<View><Text>{this.state.product.title}</Text></View>
)
}
Don't use componentWillMount() because these methods are considered legacy and you should avoid them in new code.
componentWillMount()
If your render function actually does look like you posted, then this can't work. Try chaning your render function to something like this.
render() {
const { product } = this.state
if (!product || !product.title) {
return null
}
return (
<View><Textstyle={styles.name}>product.title</Text></View>
)
}
I'm trying to call a localhost API that i created in my react app class. This API will return a list of json data, i'm trying to save these results in a property
I don't know much about Reacjs. What i have tried so far is to create a method that will call the API and return the data, the i call this method in my class and save the results in a property.
The type of this method is Promise since the results that i'm expectibng are a list of data :
let items: any[];
function getIncidentsFromApiAsync(): Promise<any[]>{
return fetch('http://localhost:3978/calling')
.then((response) => response.json())
}
export class App extends React.Component<{}, IDetailsListCustomColumnsExampleState> {
constructor(props: {}) {
super(props);
getIncidentsFromApiAsync().then(json => items = json);
}
}
I haven't been able to see the results since items is always undefined after calling getIncidentsFromApiAsync() method.
You can handle this in React using State and lifecycle method componentDidMount that gets called when the component is ready:
function getIncidentsFromApiAsync(): Promise<any[]>{
return fetch('http://localhost:3978/calling').then(
(response) => response.json()
);
}
export class App extends React.Component<{}, IDetailsListCustomColumnsExampleState> {
constructor(props: {}) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
getIncidentsFromApiAsync().then(json => this.setState({ items: json });
}
render() {
if (this.state.items.length) {
const itemsList = this.state.items.map((item) => <li key={item}>{item}</li>);
return (
<div>
<ul>{itemsList}</ul>
</div>
);
}
return <div>List is not available</div>;
}
}
JSON.Parse throws errors!
I am currently passing data in through a socket, and I can successfully read the data, but using it as a json object is giving me a lot of troubles. Here is my code
import React, { Component } from "react";
import socketIOClient from "socket.io-client";
class App extends Component {
constructor() {
super();
this.state = {
response: false,
endpoint: "http://127.0.0.1:4001"
};
}
componentDidMount() {
const { endpoint } = this.state;
const socket = socketIOClient(endpoint);
socket.on("message", mi => this.setState({ response: mi }));
}
render() {
const { response } = this.state;
const data = response.cc
return (
<div style={{ textAlign: "center" }}>
{
JSON.stringify(data)
}
</div>
);
}
}
export default App;
I am using jsonfile to read the file and check for changes, if so, push them through. Without using the JSON.stringify function, the page I am currently working in throws an error " If you meant to render a collection of children, use an array instead."
Reason why it is throwing the error is, because the initial value is boolean, and you are trying to run loop on any property of boolean, Here:
const { response } = this.state; // response = false
const data = response.cc // data will be undefined
data.map(.....) // can't read property map of undefined
Solution:
1- One option is skip the rendering until you didn't get the data from server.
Like this:
render(){
if(!this.state.response)
return <div>Loading...</div>
return(....)
}
2- Other option is, define the response as an object instead of boolean in the state, and use || [] with response.cc.
Like this:
constructor() {
super();
this.state = {
response: {},
endpoint: "http://127.0.0.1:4001"
};
}
Render the array using #array.map, Like this:
render() {
const { response } = this.state;
const data = response.cc || [];
return (
<div style={{ textAlign: "center" }}>
{
data.map(el => (
<div key={el.id}>
<p>Rank: {el.rank}</p>
<p>Price: {el.price_usd}</p>
</div>
))
}
</div>
);
}