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}
Related
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>
);
}
}
The code is working with the property 'name', names appear correctly on the map.
I wanted to enrich the json file with datas coming from my mysql database (like, add the name of countries in french or spanish for example).
I added a state 'countries' which will be initialized with json file converted in object. I fetch data from my sql database and then I set the state 'countries' with data I wanted to add.
Here is the code :
import React, { Component } from "react"
import {
ComposableMap,
ZoomableGroup,
Geographies,
Geography,
} from "react-simple-maps"
import ReactTooltip from "react-tooltip"
import jsonWorldMap from "./maps/world-50m.json"
const wrapperStyles = {
width: "100%",
height: "100%",
backgroundColor: "#0565A1"
}
class WorldMap extends Component {
constructor(){
super()
this.state = {
zoom: 1,
color: "#39464E",
countries: jsonWorldMap
}
}
componentDidMount() {
//get all countries in db
fetch('http://localhost:3001/countries')
.then(res => res.json())
.then(body =>
body.data.forEach(function(elementSql){
jsonWorldMap.objects.units.geometries.forEach(function(elementJson){
if(elementSql.alpha3 == elementJson.id)
{
elementJson.properties.nameFr = elementSql.name_fr;
}
})
})
)
this.setState({ countries: jsonWorldMap }, () => console.log(this.state.countries))
}
render() {
return (
<div style={wrapperStyles}>
<ComposableMap>
<ZoomableGroup center={[0,20]}>
<Geographies geography={this.state.countries}>
{(geographies, projection) => geographies.map((geography, i) => geography.id !== "ATA" && (
<Geography
className="Geography"
key={i}
data-tip={geography.properties.nameFr}
geography={geography}
projection={projection}
/>
))}
</Geographies>
</ZoomableGroup>
</ComposableMap>
<ReactTooltip />
</div>
)
}
}
export default WorldMap
So you can see that I added a component to have a console.log at the end of the component. See what console.log gives :
So you can see that the property 'nameFr' is present in the state object 'countries'. But, If I try to display it as tooltip, it doesn't work. And it works perfectly with property 'name' (in data-tip)
If data-tip={geography.properties.name} works fine but data-tip={geography.properties.nameFr} does not, then it seems that the problem is with state.
See your componentDidMount method. You are updating state with jsonWorldMap at the end of this method.
But as fetch is async , at that moment jsonWorldMap may not be updated yet. So I think you should move that line inside fetch. please see below:
componentDidMount() {
const _this = this; // hold this inside _this
//get all countries in db
fetch('http://localhost:3001/countries')
.then(res => res.json())
.then(body => {
body.data.forEach(function(elementSql){
jsonWorldMap.objects.units.geometries.forEach(function(elementJson){
if(elementSql.alpha3 == elementJson.id)
{
elementJson.properties.nameFr = elementSql.name_fr;
}
})
});
_this.setState({ countries: jsonWorldMap }, () => console.log(this.state.countries)); //making sure setting updated jsonWorldMap to state
}
)
}
hope it helps.
thanks
Wrap Geography with an element that uses data-tip as a props.
<div data-tip={geography.properties.nameFr}>
<Geography ... />
</div>
In order to <Geography data-tip={props.nameFr}/> work, Geography component need to use the data-tip property internaly, something like:
function Geography(props) {
return <h1 data-tip={props['data-tip']}>I'm a map</h1>;
}
To solve your problem you need to attach data-tip property to Geography wrapper, for example:
function Geography(props) {
return <h1>I'm a map</h1>;
}
function ComponentWithTooltip({ props }) {
return (
<div data-tip="nameFr">
<Geography />
</div>
);
}
function App() {
return (
<>
<Geography data-tip="hello-world" /> // Your way, won't work
<ComponentWithTooltip /> // Work
<div data-tip="nameFr2"> // Work
<Geography />
</div>
// Works with div wrapper, without won't work.
{geographies.map((geofraphy, i) => (
<div key={i} data-tip={geofraphy.properties.nameFr}>
<Geography />
</div>
))}
<ReactTooltip />
</>
);
}
Check out the demo with all use cases:
I am using cdn for react
Actually I have two JSON FILE,
abc.json
[
{
"apiKey":"642176ece1e7445e99244cec26f4de1f"
}
]
reactjs.json
[
{
"642176ece1e7445e99244cec26f4de1f": {
"src": "image_1.jpg",
"id" : "1"
}
}
]
I actually want that first of all I get apiKey from the first json file and after with the help of it i like to get the value of src
1) How can I do this in React using axios?
2) Is that Possible that we can directly get the src from reactjs.json ? If yes then How?
What I tried, but it gives error..
class FetchDemo extends React.Component {
constructor(props) {
super(props);
this.state = {
images: [],
api:[]
};
//this.listImages = this.listImages.bind(this);
}
componentDidMount() {
axios.get('abc.json').then(res => {
console.log(res);
this.setState({ api: res.data });
});
axios.get('reactjs.json').then(res => {
console.log(res);
this.setState({ images: res.data });
});
}
render() {
return (
<div>
{this.state.api.map((api, index) => (
<Pictures key={index} apikeys={api.apiKey} />
))}
</div>
);
}
}
class Pictures extends React.Component {
render() {
return (
<h1>
alt={this.props.apikeys}
</h1>
{this.state.images.map((images, index) => (
<h1 key={index}> apikeys={images.+`{this.props.apikeys}`+.src} </h1>
//Error at this point
))}
);
}
}
ReactDOM.render(
<FetchDemo/>,
document.getElementById("root")
);
Using axios you are making a request. This means that your JSON would be served from a end point. If you really need to require the json file in this fashion try importing
import abc from './abc.json';
componentDidMount = () => {
this.setState({
json: abc
})
}
I have the following JSON data:
[
{"ID":1,"Latitude":"-41.276253","Longitude":"173.283842","Image":"Church.jpg"},
{"ID":2,"Latitude":"-41.267783","Longitude":"173.279114","Image":"Centre.jpg"}
]
I am trying to import it so it can be rendered & started with the following code:
componentDidMount() {
fetch('/home/briefsJson').then(response => response.json()).then(data => {
console.log(data);
this.setState({
latitude: data.Latitude,
longitude: data.Longitude,
image: data.Image
});
});
}
This doesn't as the data is multidimensional/nested. But every example I've found is using better structured data with top level names.
How can I use setState & render to display this data?
If you want to import json from a js file you would do it like this.
Data.js
const Data = [
{"ID":1,"Latitude":"-41.276253","Longitude":"173.283842","Image":"Church.jpg"},
{"ID":2,"Latitude":"-41.267783","Longitude":"173.279114","Image":"Centre.jpg"}
]
export default Data
Then import it where you want to use it. Now you can map through the data as you like.
App.js
import Data from './data'
import React, {Component} from 'react'
class App extends Component {
state = { Data:[] }
componentDidMount() { this.setState({ Data: Data }) }
render() {
return(
<div> {this.state.Data.map(item => <div> The id is: {item.ID} </div> }</div>
)
}
}
Maybe you want to change the names of the items, and return a new data structure with less attributes this is how you would do it.
componentDidMount() {
fetch('/home/briefsJson').then(response => response.json()).then(data => {
const newData = data.map(item => {
latitude:item.Latitude,
longitude: item.Longitude,
image: item.Image}
}
this.setState({
Data:newData
});
});
}
Now if you want to display this data in render.
renderData = () => {
return (
<div>
{this.state.Data.map(item => (
<div>
{item.latitude}
{item.longitude}
<img src={item.img} />
</div>
)}
</div>
)
}
render() {
return (
<div> {this.renderData()} </div>
)
}
It would probably just be easier to first construct what you want, then do a mapping in the rendering. It looks like you don't even need to do anything to convert it, since the json data is exactly what you want in the first place. So:
componentDidMount() {
fetch('/home/briefsJson').then(response => response.json()).then(data => {
console.log(data);
this.setState({ data });
});
}
render() {
return (
<div>
{this.state.data.map(datum => (element))}
</div>
);
}
Feels like I'm missing something obvious here - but I can't figure out how to access my JSON data. I have a Container component:
class About extends Component {
componentDidMount(){
const APP_URL = 'http://localhost/wordpress/'
const PAGES_URL = `${APP_URL}/wp-json/wp/v2/pages`
this.props.fetchAllPages(PAGES_URL, 'about')
}
render(){
return (
<div>
<Header/>
<div className="bg">
<div className="home-wrapper">
<h1>AAAAABBBBBOOOOUUUUUT</h1>
<Counter/>
<AboutInfo />
</div>
</div>
<Footer/>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchAllPages }, dispatch)
}
export default connect(null, mapDispatchToProps)(About);
And a Smart component:
class AboutInfo extends Component {
render(){
console.log(this.props.page);
console.log(this.props.page.id);
return (
<div>
<h1>This is ID: {this.props.page.id}</h1>
</div>
)
}
}
const mapStateToProps = ({ page }) => {
return { page }
}
export default connect(mapStateToProps)(AboutInfo);
My action:
export const fetchAllPages = (URL, SLUG) => {
var URLEN;
if(!SLUG){
URLEN = URL
} else {
URLEN = URL + "?slug=" + SLUG
}
return (dispatch) => {
dispatch(fetchRequest());
return fetchPosts(URLEN).then(([response, json]) => {
if(response.status === 200){
if(!SLUG) {
dispatch(fetchPagesSuccess(json))
} else {
dispatch(fetchPageBySlugSuccess(json))
}
} else {
dispatch(fetchError())
}
})
}
}
const fetchPageBySlugSuccess = (payload) => {
return {
type: types.FETCH_PAGE_BY_SLUG,
payload
}
}
My reducer:
const page = (state = {}, action) => {
switch (action.type) {
case FETCH_PAGE_BY_SLUG:
console.log(action.paylod)
return action.payload
default:
return state
}
}
This gives me:
When I console.log(this.props.page) in my AboutInfo component, it prints the object, but when I print console.log(this.props.page.id) it gives me undefined. Why can't I print the JSON content? Thanks!
page is an array and hence this.props.page.id is undefined. You might want to access the first element in array in which case you would do
this.props.page[0].id
but you might also need to add a test, since before the response is available you will be trying to access page[0].id and it might break.
You could instead write
this.props.page && this.props.page[0] && this.props.page[0].id
Getting data from the store is async So you must adding loading varibale on your reducer
class AboutInfo extends Component {
render(){
if(this.props.loading) return (<div>loading</div>);
return (
<div>
<h1>This is ID: {this.props.page.id}</h1>
</div>
);
}
}
const mapStateToProps = ({ page, loading }) => {
return { page, loading }
}
on your action try returing
json.page[0]
That is because page is an array and the id is a property of its 1st element.
So use this.props.page[0].id
If the logged object in your screenshot is the this.props.page then you will need and additional .page as that is also a part of the object this.props.page.page[0].id