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".
Related
Best regards. This is my first question. I am new to react and I do not know how to do with this doubt.
I make a request and I keep the answer in the state. Now I can't deserialize the json and use it within the app. I have tried several ways that recommend online but nothing. if I make a json.stringify, I can see the information, that is, the request is correct.
this is the request I receive from the api:
{"boards":[{"items":[{"id":"John Smith","column_values":[{"text":"Caracas, Distrito Capital, Venezuela"}]},{"id":"Edith Ruza","column_values":[{"text":"Buenos Aires, CABA, Argentina"}]},{"id":"david Rios","column_values":[{"text":"Perth Australia Occidental, Australia"}]},{"id":"Peter Doe","column_values":[{"text":"Calgary, Alberta, Canadá"}]},{"id":"Mary Jones","column_values":[{"text":"London, Reino Unido"}]},{"id":"Lionel Messi","column_values":[{"text":"París, Francia"}]},{"id":"Samy Forte","column_values":[{"text":"Mexico City, CDMX, México"}]},{"id":"Tadeo Carthy","column_values":[{"text":"Tel Aviv, Israel"}]}]}]}
and this is my code that not work:
class App extends React.Component {
constructor(props) {
super(props);
// Default state
this.state = {
setData:{},
settings: {},
myData: {},
};
}
com
componentDidMount() {
monday
.api('query { boards( ids : 2664704591 ) { items { id : name column_values(ids : "ubicaci_n") { text }}}}')
.then(res => {this.setState({myData: res.data})});
}
render() {
return (
<div className="App">
<AttentionBox
title="hola"
text="Let's start building your amazing app, which will change the world!"
/>
<div className="btn btn-primary"></div>
<button className="btn btn-warning"></button>
<div>
{this.state.myData.map((property) => {
return (<div>property</div>)
})}
</div>
</div>
);
}
}
export default App;
I would greatly appreciate any clue to move forward
As #ChrisG mentioned, you first need to parse the json string (provided it's a valid json string—it appears to be) into a js object by using JSON.parse(res.data), then you have 2 levels of nesting in your data structure over which you'd have to map on:
{ this.state.myData?.boards && // however you want to guard this part
<div>
{this.state.myData.boards.map(board =>
<div>{board.items.map(item => <div>{item}</div>)}</div>
}
</div>
}
Another note is that because myData could either be {} or have { boards: [] }, you should have some form of guard on whether myData has been returned. This could be an if statement, or it could use javascript's falsey logic with a check && result syntax as I used above.
A couple more (opinionated) tips because you're new:
Try to switch to the functional components rather than the class-based components you've used here
You're probably going to want to specify a few lower level components to deal with these mappings so your code doesn't get extremely cluttered
I need to loop through an array that is nested within an array in my React State. I see the state is populated with both arrays in Dev Tools, also, when I loop through the parent array with Object.keys(this.state.products).map I get all of the values. The issue is when I try to loop over the child array, or pull any value from the child array, such as this.state.products[0][3] I get Undefined errors.
Whats more, is when I console.log this.state.products[0][3] in ComponenetDidUpdate I get the value, so it's like React is setting the state but not all the way?
const { Component } = React;
class App extends Component {
state = { products: [] };
componentDidMount() {
this.apiSearch();
}
apiSearch = () => {
// api call is made here
const API = `http://localhost:5000/products`;
fetch(API)
// api response log
// .then(response => console.log(response))
.then(response => response.json())
.then(data => {
this.setState({ products: data }, () =>
this.setState({ products: data })
);
})
.catch(err => {
console.log(err);
});
};
render() {
return (
<div className="App">
{<div>{typeof this.state.products[0]}</div>}
{/* issue is here !!!! */}
{<div>{typeof this.state.products[0][3]}</div>}
{/* issue is here !!!! */}
{this.state.products.forEach(function(element) {
console.log(element);
})}
<br></br>
Listings:
{Object.keys(this.state.products).map((keyName, i) => (
<li className="listingItem_Parent" key={i}>
<span className="listingItem">
{i} Name: {this.state.products[keyName]}
</span>
</li>
))}
<br></br>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I need to iterate through the child array in "products" which is set in state and generate a nice list of them like with map in the first array.
Your component's render is called before the fetch occurs. At that point, your state just contains { products: [] }, so naturally this.state.products[0][3] fails because this.state.products[0] is undefined at that point. You need to either initialize your state such that this.state.products[0][3] is meaningful, or update render so that it doesn't fail when there is nothing in this.state.products.
There are a fair number of possibilities. First, check the console output, it is likely you're getting a warning explaining what's going on.
First, check that the state is really an array and not an error or some other object.
Secondly, you are explicitly checking something out of bounds in the initial state (the other answer just posted suggested this).
Third, make sure you use an key/ID when iterating over the list, it might not work without it (it should generate a warning).
//something like this
{ this.state.products.map( p => {
return (<span key={p.id}>{p.name /*whatever*/}</span>)
} )
Basically make sure that render method works with any and all data, right now there are no checks in place to validate the source so you might have something unexpected crashing your application. The hard coded index out of bounds is highly suspect.
componentDidMount() runs after the first render. So, the fetch is not even done by the time the first return is executed, which means this.state.products is still an empty array.
And you are performing actions on an empty array at this moment. Which is why
this.state.products[0][3] will fail.
At this moment:
this.state.products =[]
this.state.products[0] = undefined
this.state.products[0][3] -> error since you are checking for [3] of undefined.
You need to check if the array is empty and then perform actions based only when it's not.
I have a situation like this :
I want to output an array of React components based on a firebase database request.
class component exends React.component {
constructor () {
...
firebase.database().ref( '/properties/' ).once( 'value' ).then(
snapshot =>
{
var obj = snapshoot.val();
arr = Object.getOwnPropertyNames( obj )
.map( key => { name: obj[key].name, ... } );
this.setState( { elements : arr } );
}
);
...
}
...
render () {
return(
<Container>
{this.state.elements.map(el => <Element {...el} />)};
</Container>
);
}
...
}
At runtime, I find that the <Container> component has an extra element. However, when I check the size of the array just before the render function, the size is correct.
I know that the problem comes from the firebase database request because when I replace the data by order data that do not come from the firebase database everything behaves correctly.
The most astonishing is that if build the array inside the firebase request function callback without using any data providing from the firebase database the same problem arise again.
Any idea?
I fact the extra element was added by a lower level SizeMe component. I don't know why the problem only occurs with the firebase objects, However, when I wrap the content of the SizeMe component with a <div> I got rid of the extra element at the start of the array.
Hey I have been trying to figure out a fix for this for a while, I've searched here and all over but I've found nothing.
I have an API, it contains nested arrays, and is given through JSON.
I am able to get the contents inside the JSON to display when I use the console.log, but when I try to map the arrays, I constantly get errors stating that there's an issue with the .map function.
From what I have seen, it usually happens because it doesn't work with a string, but it's not being applied to a string from what I can tell...
There's a lot of data in the API, but I'm sure once I know how to get just one, eg the date, to display then I'll be fine with the rest.
Here is the code
export default class App extends React.Component {
constructor() {
super();
this.state = {
myItem: []
};
}
componentDidMount() {
this.getItems();
}
getItems () {
fetch('MYAPIGOESHERE')
.then(results => results.json())
//THIS BELOW WORKS
.then(results => console.log(results.date, results.location));
//THIS BELOW AND THE RENDER DOES NOT
.then(myItem => this.setState({myItem}))
}
render() {
return (
<div>
{this.state.myItem.map (myItem =>
<div> {myItem.date} </div> )}
</div>
)
})
}
}
Thank you!
{"date":"2018-09-16T11:22:00.000Z","location":"Cardiff City Stadium","teams":[{"name":"Cardiff City","homeTeam":true,"formation":"4-4-2","players":[{"playerId":"5afc0b73b481e9b536c4727b","position":"GK"},{"playerId":"5afc1377b481e9b536c4727c","position":"RB"},{"playerId":"5afc188bb481e9b536c47299","position":"CB"},{"playerId":"5afc188ab481e9b536c47297","position":"CB"},{"playerId":"5afc1872b481e9b536c4727e","position":"LB"},{"playerId":"5afc1873b481e9b536c4727f","position":"RM"},{"playerId":"5afc1874b481e9b536c47280","position":"CM"},{"playerId":"5afc1876b481e9b536c47281","position":"CM"},{"playerId":"5afc1876b481e9b536c47282","position":"LM"},{"playerId":"5afc1876b481e9b536c47283","position":"FW"},{"playerId":"5afc1877b481e9b536c47284","position":"FW"}]},{"name":"Swansea City","homeTeam":false,"formation":"4-3-3","players":[{"playerId":"5afc187ab481e9b536c4728a","position":"GK"},{"playerId":"5afc1878b481e9b536c47286","position":"RB"},{"playerId":"5afc1879b481e9b536c47289","position":"CB"},{"playerId":"5afc187ab481e9b536c4728b","position":"CB"},{"playerId":"5afc187bb481e9b536c4728c","position":"LB"},{"playerId":"5afc1879b481e9b536c47288","position":"RM"},{"playerId":"5afc1878b481e9b536c47287","position":"CM"},{"playerId":"5afc187bb481e9b536c4728d","position":"LM"},{"playerId":"5afc187cb481e9b536c47290","position":"FW"},{"playerId":"5afc187db481e9b536c47291","position":"FW"},{"playerId":"5afc187db481e9b536c47292","position":"FW"}]}]}
You can't map over this.state.myItem because it's an object.
You could do:
<div>{this.state.myItem.date}</div>
If you have an array of objects, you can do:
<div>{this.state.myItems.map(e => <div>{e.date}</div>)}</div>
for example.
If you have an object with an array in it, you can do:
const { teams } = this.state.myItem;
<div>{teams.map(t => <div>{t}</div>)}</div>
The data you have is like this: Object --> Array --> Object, so you can combine the above to display the data you want.
Try wrapping setState in braces:
.then(myItem => {
this.setState({myItem})
})
Map function works with arrays, you seem to have a single object. Try this:
return (
<div> {this.state.myItem.date} </div>
)
and remove the space between map and the opening parentheses.
Good afternoon,
I am trying to display data that is provided to my application by an instance of MongoDB before the initial render. I have yet been successful in doing so either running into errors or warnings.
This is the part of the render method I am working with.
<div className="right-column">
<div className="pipeline">
{this.props.tournamentList.map((x,i) => {
return <li key={i}>{x.name}</li>
})}
</div>
</div>
this.props.tournamentList has a value of an array of objects like so:
tournamentList:
Array[15]
0:
{…}
1:
{…}
2:
{…} ...
This list comes to my application through the componentWillMount lifecycle method, so before the initial render. To me I should be able to iterate through the array and make a dynamically generated list of tournaments provided by my database.
Yet with the code I provided I am getting this warning:
Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {prelims, outRounds, notes}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method ofBuildTournament.
I tried this approach, creating called displayTournaments and calling it inside the div with the class of "pipeline" but nothing happens, no errors no render:
displayTournaments(){
const { tournamentList } = this.props;
tournamentList.map((x,i) => {
return <li key={i}>{x.name}</li>
})
}
Clearly I am doing something wrong but I don't know what. Is this an instance where I should be using keyed fragments as suggested by the error message? Would anyone smarter than myself be willing to lend some insight?
Cheers.
Update:
Sorry, I misunderstood your question. Kyle is correct with the loading state.
In addition, using a library like lodash will allow you to map over objects in a more natural manner. The native javascript map method doesn't handle objects all that well.
https://www.npmjs.com/package/lodash
you use it much the same way. just
import _ from lodash
then
_.map(objectToMap, (x) => <Component key={x}>{x.thing}</Component>)
Here would be a simple solution that would have a loading state, error state, and success state.
The first thing to note is you will need to use Object.keys() to your object in order to map over the array of keys since you cannot map plain objects. You should also note that the map will return the key of each object so in order to target key values pairs you will need to use a syntax like this tournaments[key].name rather than just doing tournament.name as you are targeting an object with in an object and then grabbing the value.
Let me know if you need any more help with this
import React from 'react'
import Loader from '../Loader'
const resultList = ({ tournaments, isFetching = true }) => (
<div>
{
isFetching
? <div>
<Loader /><br />
<span>Loading…</span>
</div>
: <div>
{
Object.keys(tournaments).length
? <div>
{
tournaments.map((key) => (
<section id={tournaments[key].id} key={key}>
<p>{tournaments[key].name}</p>
</section>
))
}
</div>
: <div>
<p>There are no tournaments....</p>
</div>
}
</div>
}
</div>
);
export default resultList
You are going to need to have a loading state if you get your data in the componentWillMount or componentDidMount lifecycle hooks. The below example will illustrate how this is done.
class ComponentThatGetsAsyncData extends PureComponent {
constructor( props ) {
super( props );
this.state = {
tournamentList: [ ]
}
}
componentDidMount() {
// use any http library you choose
axios.get( "/some_url" )
.then( ( res ) => {
// this will trigger a re-render
this.setState({
tournamentList: res.data
});
})
.catch( ( err ) => {
// handle errors
});
}
render() {
const { tournamentList } = this.state;
// i'd use something other than index for key
// on your initial render your async data will not be available
// so you give a loading indicator and when your http call
// completes it will update state, triggering a re-render
return (
{
tournamentList ?
tournamentList.map((x,i) => {
return <li key={i}>{x.name}</li>
}) :
<div className="loading">Loading...</div>
}
);
}
}