Load external JSON in React - javascript

So I quite new to this. I want to load a external json file into react, and then use it.
The problem apparently is that the json hasn't been yet loaded, so I get errors. Like Cannot read property 'map' of undefined etc. The console.log give:
1
3
Uncaught TypeError: Cannot read property 'map' of undefined
So I've read this has to do with asynchronous things. But I can't find any example on how to fix it. Or how to make it work.
I would really appreciate it to see a small example like this, to make it work.
Later on I want to make it possible to filter the json with <input type=text etc> with some kind of dynamic search. But first things first. So can someone help me out?
This is my simple file:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.state = {
data: []
};
console.log('1');
};
componentDidMount() {
fetch("http://asite.com/file.json")
.then( (response) => {
return response.json() })
.then( (json) => {
this.setState({data: json});
console.log('2');
})
};
render() {
console.log("3");
return(
<div className="Employee">
{
this.state.data.employees.map(function(employee) {
return (
<div>
<h1> {employee.name} </h1>
</div>
)
})
}
</div>
)
}
}
export default App;

Since you have this.state.data.employees, I would assume you want to shape the initial state like this:
constructor(){
super();
this.state = {
data: {
employees: []
}
};
};

You can either choose to save to the state another variable which is flipped when you know you've loaded the data, or just check to ensure the data exists before trying to map it.
The latter could be done like below:
<div className="Employee">
{ this.state.data.employees &&
this.state.data.employees.map(function(employee) {
return (
<div>
<h1> {employee.name} </h1>
</div>
)
})
}
</div>
Or you could adjust the initial state to include an empty (but initialized) version of the return such as:
this.state = {
data: { employees: [] }
};
Checking to ensure the state contains the data field you're mapping is much safer though incase the return ever doesn't include the field.

Related

Fetch and array of objects from a database and displaying it with react

I was trying to fetch an array of objects when I encounter this problem. Sorry for my messy code, I'm a beginner.
export class App extends Component {
state ={
character:[]
}
componentDidMount(){
fetch('https://swapi.dev/api/people/').then(data => data.json()).then(res =>{
this.setState(() =>{
const ar = res.results
return {
character: ar
}
})
})
}
render() {
return (
<div>
test
{console.log(this.state.character[0])}
</div>
)
}
}
That code works fine for me at first until I changed the console log to {console.log(this.state.character[0].name)} it says "cannot read property of an undefined" even though the first line displays the object perfectly
Your App component fetches the data in componentDidMount life cycle method which will execute after the first mount. {console.log(this.state.character[0].name)} shows error "cannot read property of undefined because when the component mounts for first time, the data is not available yet, which means this.state.character is still an empty array. So when try to access this.state.character[0].name, its like saying give me the value of undefined.name which will give you an error because name property does not exist on undefined. To fix this issue you can check if character.length in a conditional and then try to access .name.
Example
render() {
return (
<div>
test
{this.state.character.length && <p>{this.state.character[0].name}</p>}
</div>
)
}
Fetch is asynchronous so you will take some time to get the result but before that your jsx is called already. So it is good to use conditional jsx. Please check the complete example as below where I shown all the name of character array under li element.
export class App extends React.Component {
state = {
character: []
};
componentDidMount() {
fetch('https://swapi.dev/api/people/').then(data => data.json()).then(res => {
this.setState(() => {
const ar = res.results;
return {
character: ar
}
})
})
}
render() {
return (
<div>
test
{
this.state.character && this.state.character.length && this.state.character.map((item, index) => {
return <li key={index}>{item.name}</li>
})
}
</div>
)
}
}
export default App;
So that will not work. Data is not there yet. There are bunch of ways to workaround it. Most simple one is to have object structure predefined in the state object, which is not very dynamic. Otherwise simple .length + if + else will do the job.

Map is undefined in React

I am using fetch to get API data that I'm using to create a drop down that I will add routes too. I've done this a couple of times before but I used axios previously but I just wanted to get familiar with fetch as well. Can anyone see the problem of why map would be undefined?
import React, { Component } from 'react'
class Fetchheroes extends Component {
constructor() {
super();
this.state = {
heroes: [],
}
}
componentDidMount(){
fetch('https://api.opendota.com/api/heroStats')
.then(results => {
return results.json();
}).then(data =>{
let heroes = data.results.map((hero) =>{
return(
<div key={hero.results}>
<select>
<option>{hero.heroes.localized_name}</option>
</select>
</div>
)
})
this.setState({heroes: heroes});
console.log("state", this.state.heroes);
})
}
render(){
return(
<div>
<div>
{this.state.heroes}
</div>
</div>
)
}
}
export default Fetchheroes
You have a bad mapping about data. You need to use data instead of data.result and you have a bad key value because results are not unique key in that case. You also don't need your hero.heroes.localized_name just hero.localized_name. I made an example in codesandbox.
https://codesandbox.io/s/clever-hodgkin-7qo6p
Edit
I made another example when I put all records to one select, not for multiple selects, maybe is that what you need or someone else :).
https://codesandbox.io/s/bold-grass-gv0wc

Fetch one JSON object and use it reactjs

I'm very new to JS and ReactJS and I try to fetch an endpoint which gives me a JSON Object like this :
{"IDPRODUCT":4317892,"DESCRIPTION":"Some product of the store"}
I get this JSON Object by this endpoint :
http://localhost:3000/product/4317892
But I dont how to use it in my react application, I want to use those datas to display them on the page
My current code looks like this but it's not working and I'm sure not good too :
import React, {Component} from 'react';
class Products extends Component {
constructor(props){
super(props);
this.state = {
posts: {}
};
};
componentWillMount() {
fetch('http://localhost:3000/product/4317892')
.then(res => res.json())
.then(res => {
this.setState({
res
})
})
.catch((error => {
console.error(error);
}));
}
render() {
console.log(this.state)
const { postItems } = this.state;
return (
<div>
{postItems}
</div>
);
}
}
export default Products;
In the console.log(this.state) there is the data, but I'm so confused right now, dont know what to do
Since I'm here, I have one more question, I want to have an input in my App.js where the user will be able to type the product's id and get one, how can I manage to do that ? Passing the data from App.js to Products.js which is going to get the data and display them
Thank you all in advance
Your state doesn't have a postItems property which is considered undefined and react therefore would not render. In your situation there is no need to define a new const and use the state directly.
Also, when you setState(), you need to tell it which state property it should set the value to.
componentWillMount() {
fetch('http://localhost:3000/product/4317892')
.then(res => res.json())
.then(res => {
this.setState({
...this.state, // Not required but just a heads up on using mutation
posts: res
})
})
.catch((error => {
console.error(error);
}));
}
render() {
console.log(this.state)
return (
<div>
<p><strong>Id: {this.state.posts.IDPRODUCT}</strong></p>
<p>Description: {this.state.posts.DESCRIPTION}</p>
</div>
);
}
I have got 3 names for the same thing in your js: posts, postItems and res.
React can not determine for you that posts = postItems = res.
So make changes like this:
-
this.state = {
postItems: {}
};
-
this.setState({
postItems: res
});
-
return (
<div>
{JSON.stringify(postItems)}
<div>
<span>{postItems.IDPRODUCT}</span>
<span>{postItems.DESCRIPTION}</span>
</div>
</div>
);
{postItems["IDPRODUCT"]}
Will display the first value. You can do the same for the other value. Alternatively, you can put
{JSON.stringify(postItems)}
With respect to taking input in the App to use in this component, you can pass that input down through the props and access it in this component via this.props.myInput. In your app it'll look like this:
<Products myInput={someInput} />

Accessing data from Axios GET request

I'm having trouble accessing data from an Axios GET request. I'd like to be able to iterate through all of the data I get and create a table that displays the username, avatar, and score for each user. The only way I'm able to currently render a single username is with the following code:
this.setState({name: res.data[0].username});
But the above code only gives me access to the first object in the URL I use in the GET request. If I use this code:
this.setState({name: JSON.stringify(res.data)});
I'm able to render the entire object as a string. So that makes me think I need to update my render method, but I'm not sure how.
What steps should I take so that I can map over the state that I'm setting and render each user's data in a table or list?
class LeaderBoard extends React.Component {
constructor(props) {
super(props)
this.state = {
name: []
}
}
componentDidMount(){
axios.get('https://fcctop100.herokuapp.com/api/fccusers/top/recent').then(res =>
{
this.setState({name: res.data[0].username});
});
}
render () {
return (
<div>
<h1>{this.state.name}</h1>
</div>
)
}
}
ReactDOM.render(
<LeaderBoard/>, document.getElementById("app"));
You're on the right track, you're just missing a few key pieces.
Step 1: Add the entire array to your state
I'm not sure of the exact structure of your data, but you likely want to do this
componentDidMount(){
axios.get('https://fcctop100.herokuapp.com/api/fccusers/top/recent').then(res =>
{
this.setState({data: res.data});
});
}
Now you have all the data you want to work with in your state, ready to be iterated over.
Step 2: Use .map() to create the JSX elements you want
This code here:
render () {
return (
<div>
<h1>{this.state.name}</h1>
</div>
)
}
}
The big fault here is that it will only ever render one thing, because, well, that's all there is. What you want to do is .map() through and create a bunch of names from your data, right?
That would look more like this.
render () {
const namesToRender = this.state.data.map(val => {
return (
<h1>val.username</h1>
)
})
return (
<div>
{namesToRender}
</div>
)
}
}
All this is saying is "Go through that data and give me the name of each person and wrap it in some <h1> tags and spit that out".

React API call failure

I'm new to React and I'm trying to build (for now) a simple app that allows you to search for a name in a list of transactions and return the transaction details to you. So far I have been struggling to deal with the API request and I keep running into errors. I'm using superagent for my API calls
import React, {Component } from 'react';
import Request from 'superagent'
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {};
}
componentWillMount() {
var url = 'https://api.myjson.com/bins/2dorw';
Request.get(url).then((response) => {
this.setState({
items: response.body.items,
});
});
}
render() {
var names = this.state.items.map((name, index) => {
<div>
<li key={index}> {this.state.items.merchant.name } </li>
</div>
});
return (
<div className="App">
{names}
</div>
);
}
}
export default App;
I can't even make this simple API call to work, just display the names from the API endpoint. This has been going on for a day or so, and usually I get the object either null, undefined and type errors.
Uncaught (in promise) TypeError: Cannot read property '_currentElement' of null(…)
App.js:64 Uncaught TypeError: Cannot read property 'map' of undefined
I have tried following tutorials but I haven't been able to find one that would work in my case, so I can't wrap my head around the API call.
UPDATE
After changing to componentDidMount(), adding isFetching and setting items to an array in setState (as the two answers have suggested), I am now getting an
Uncaught (in promise) TypeError: Cannot read property 'name' of undefined
Using componentWillMount() is slightly early in the lifecycle. Wait until it has mounted using componentDidMount() That should help resolve the first error being thrown.
The second error comes from this.state.items.map when the items have not shown up yet. I would also suggest adding a isFetching state, to check if the data has shown up yet.:
class App extends Component {
constructor() {
super();
this.state = {
isFetching: true
};
}
componentDidMount() {
var url = 'https://api.myjson.com/bins/2dorw';
Request.get(url).then((response) => {
this.setState({
items: response.body.items,
isFetching: false
});
});
}
render() {
if (this.state.isFetching) return null;
var names = this.state.items.map((item, index) => {
<div>
<li key={index}> {item.merchant.name} </li>
</div >
});
return (
<div className="App">
{names}
</div>
);
}
}
export default App;
Update
Added a slight change - looks like your map function was also a bit off. It's hard to say what exactly you are looking for, because I don't know the data structure exactly.
You had
var names = this.state.items.map((name, index) => {
<div>
<li key={index}> {this.state.items.merchant.name } </li>
</div>
});
Passing name as the first param, which is the entire item. You then call this.state all over again - which you shouldn't need to do. I think this is where some of the undefined issues are coming from as well.
this.state.items is undefined because you declared state = {}, so can not map over it. You can fix this problem by making items an empty array.
constructor() {
super();
this.state = {
items: []
};
}

Categories

Resources