I'm quite new with react, I'm trying to hit an API and I'm getting this response. I need to go over the array and show the elements in a table:
{
"people": [
{
"id": "1",
"name": "philip",
"age": 25,
"timestamp": "2020-10-17T21:59:50.151"
},
{
"id": "2",
"name": "philip2",
"age": 26,
"timestamp": "2020-10-17T21:59:50.152"
},
{
"id": "3",
"name": "philip3",
"age": 27,
"timestamp": "2020-10-17T21:59:50.153"
},
]
}
I'm hitting and getting response from the api correctly but I have some issues trying to parse it.
import React, { Component } from 'react';
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
people: []
}
}
componentDidMount() {
fetch('/local/api/people')
.then(res => res.json())
.then(json => json.people)
.then(people => this.setState({'people': people}))
}
render() {
return (
<div className="App">
{this.state.people}
Here I'd need to go over the array and show all the elements
</div>
);
}
}
export default App;
Error: Objects are not valid as a React child (found: object with keys ....... If you meant to render a collection of children, use an array instead.
I tried a lot of things but nothing worked so far
you have to map over the array inside the return like the code below and I am passing a key that's a react way to identify the element
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
and also am checking when the component renders that I will only show the list when the state people array length is true means not 0
import React, { Component } from "react";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
people: [],
};
}
componentDidMount() {
fetch("/local/api/people")
.then((res) => res.json())
.then((json) => json.people)
.then((people) => this.setState({ people: people }));
}
render() {
return (
<div className="App">
{this.state.people.length && this.state.people.map((element, key) => {
return (
<div key={key}>
<span>{element.id}</span>
<span>{element.name}</span>
<span>{element.age}</span>
<span>{element.timestamp}</span>
</div>
);
})}
</div>
);
}
}
export default App;
You can create a member function that maps over the array and creates and returns jsx for each table row. You can then call this function inside a table body tag.
renderTableData() {
return this.state.people.map((person, index) => {
const { id, name, age } = person //destructuring
return (
<tr key={id}>
<td>{id}</td>
<td>{name}</td>
<td>{age}</td>
</tr>
)
})
}
render() {
return (
<div className="App">
<table id='people'>
<tbody>
{this.renderTableData()}
</tbody>
</table>
</div>
)
}
Related
I'm learning React and have some troubles with using state and props. There is two files: App.js and component. In App.js i use axios to get JSON data from IP and store in a state. But I cannot pass the data to props through the state.
Here is App.js:
import React from 'react';
import axios from 'axios';
import Shutruk from './Shutruk';
const qwerty = {
"data": [
{
"_id": "5d1cb18e4af03042df6267c5",
"title": "Shutruk",
"description": "Shhhhhhhhhhhhh",
"date": "2019-07-03T13:45:50.850Z",
"__v": 0
},
{
"_id": "5d1cc27b37c9751001f5c12f",
"title": "Shilkhak",
"description": "Shilkhak-Inshushinak",
"date": "2019-07-03T14:58:03.797Z",
"__v": 0
},
{
"_id": "5d1cc45655780f11112a023f",
"title": "Унташ",
"description": "Untash-Napirisha",
"date": "2019-07-03T15:05:58.699Z",
"__v": 0
},
{
"_id": "5d1ef36c503601183b5f856f",
"title": "dgfdgfdhgf",
"description": "bbbbbbbbbbbbbbbbb",
"date": "2019-07-05T06:51:24.873Z",
"__v": 0
},
{
"_id": "5d1ef381503601183b5f8570",
"title": "qewytuytruytru",
"description": "jhfgasjdfgasjdfgjhsdf",
"date": "2019-07-05T06:51:45.761Z",
"__v": 0
}
]
};
class App extends React.Component {
state = {
data: []
}
componentDidMount() {
axios.get('http://localhost:5555/posts')
.then(res => {
const data = res.data;
this.setState({ data });
})
}
render() {
return (
<div>
<Shutruk name={ this.state.data.data[0].title }/>
</div>
)
}
}
export default App;
Here is the component:
import React from 'react';
class Shutruk extends React.Component {
render() {
return (
<div>
<h1>This is is {this.props.name}!</h1>
</div>
)
}
}
export default Shutruk;
I use axios to get data from backend, but when I insert it to props, it does'nt work. I create an array qwerty[] with the same data, and when I replace with:
return (
<div>
<Shutruk name={ qwerty.data[0].title }/>
</div>
)
it works correctly. What is the problem, if there is no difference between 'this.state.data' and 'qwerty'?
I checked with console.log and the result is same!
Thanks to everyone for any help!
This is because, axios and setState are asynchronous and when the component loads in componentDidMount, it takes a while to load data into state and since data.data[0] is still empty, it doesn't work. But when you use a const to declare it, it works as it is already present.
Instead of
<Shutruk name={ this.state.data.data[0].title }/>
Do:
renderShutruk = () => {
if (this.state.data.data !== undefined) {
return <Shutruk name={this.state.data.data[0].title} />
} else {
return null;
}
};
render() {
return (
<div>
{this.renderShutruk()}
</div>
);
}
Your App component is probably crashing when mounting while you're accessing an undefined state because your try to get this.state.data.data[0].title when data state equals []
Try replacing your App component like this to prevent access to an undefined state (I recommend doing this for all your asynchronous operations in the components):
class App extends React.Component {
state = {
data: [],
loading: true,
error: false,
}
componentDidMount() {
axios.get('http://localhost:5555/posts')
.then(res => {
const data = res.data.data; // get the data array instead of object
this.setState({ data, loading: false });
})
.catch(err => { // log request error and prevent access to undefined state
this.setState({ loading: false, error: true });
console.error(err);
}
render() {
if (this.state.loading) {
return(
<div>
<p> Loading... </p>
</div>
)
}
if (this.state.error || !this.state.data[0]) { // if request failed or data is empty don't try to access it either
return(
<div>
<p> An error occurred </p>
</div>
)
}
return (
<div>
<Shutruk name={ this.state.data[0].title }/> // access to data array state
</div>
)
}
}
export default App;
Just change this,
<Shutruk name={ this.state.data.data[0].title }/>
with,
{this.state.data ? <Shutruk name={ this.state.data[0].title }/> : null}
Update
If you are not getting data, then you must use async/await,
async componentDidMount() {
await axios.get('http://localhost:5555/posts')
.then(res => {
//console.log(res) => if `res` is {data:[...]} , then do this,
const data = res.data;
//console.log(res) => if `res` is {data: data: [...]} , then do this,
const data = res.data.data;
this.setState({ data });
})
}
A newbie in React here. I'm using axios to retrieve this object requested to my Django server rest framework Api:
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"url": "http://localhost:8000/blog/api/categories/1/",
"title": "Django",
"slug": "django"
},
{
"id": 2,
"url": "http://localhost:8000/blog/api/categories/2/",
"title": "Git",
"slug": "git"
},
{
"id": 3,
"url": "http://localhost:8000/blog/api/categories/3/",
"title": "Introduction to docker",
"slug": "introduction-to-docker"
}
]
}
So far I've been able to store those variables in separate arrays. This is the implementation of my FetchDemo.js:
import React, {Component} from 'react';
import axios from 'axios';
{/* https://daveceddia.com/ajax-requests-in-react/ */}
class FetchDemo extends Component {
state = {
urls: []
}
state = {
titles: []
}
state = {
slugs: []
}
componentDidMount() {
axios.get(`${this.props.url}${this.props.path}`).then(res => {
const urls = res.data.results.map(num => num.url);
const titles = res.data.results.map(num => num.title);
const slugs = res.data.results.map(num => num.slug);
this.setState( {urls} );
this.setState( {titles} );
this.setState( {slugs} );
});
}
render() {
return (
<div>
<h1>{`${this.props.url}${this.props.path}`}</h1>
<ul>
/// How to generate the JSX objects? ///
</ul>
</div>
);
}
}
export default FetchDemo;
Is it possible to build in the axios request a state array from this JSON object like the next one instead? How do I generate JSX with this new state categories?
this.setState({
categories: [
{url: "http://localhost:8000/blog/api/categories/1/", title: "Django", slug: "django"},
{url: "http://localhost:8000/blog/api/categories/2/", title: "Git", slug: "git"},
{url: "http://localhost:8000/blog/api/categories/3/", title: "Introduction to Docker", slug: "introduction-to-docker"},
]
}
)
I wish I knew more React and JavaScript. Any help is appreciated, thanks in advance.
There are a few things to note here.
state is a single object with multiple keys.
setState() is a single async transaction. When you are updating your state, you should update it in one go.
You can do things in a simpler way if I understand your question right. You don't have to store the values in multiple keys. It can be stored in a single key categories.
import React, { Component } from 'react';
import axios from 'axios';
class FetchDemo extends Component {
constructor(props) {
super(props);
this.state = {
categories = []
}
}
componentDidMount() {
axios.get(`${this.props.url}${this.props.path}`).then((res) => {
const urls = res.data.results.map((num) => num.url);
const titles = res.data.results.map((num) => num.title);
const slugs = res.data.results.map((num) => num.slug);
this.setState({
categories: res.data.results
});
});
}
render() {
const { categories } = this.state;
return (
<div>
<h1>{`${this.props.url}${this.props.path}`}</h1>
<ul>
{categories.length ?
categories.map(cat => (
<li key={cat.id}>
<div>{cat.title}</div>
</li>
))
:
(<li>categories not loaded yet</li>)}
</ul>
</div>
);
}
}
export default FetchDemo;
class Home extends PureComponent {
render() {
const { proList } = this.props;
return (
<HomeWrap>
<Banner />
<Profile />
<div className="projectWrap">
<div className="hotRecommended">
{
this.hotRecommendList(proList)
}
</div>
</div>
</HomeWrap>
)
}
hotRecommendList(list) {
let hotTarget = list.filter(item => item.tag === 'hot');
return hotTarget.map((target, index) => {
return (
<Project key={index} />
)
})
}
}
the error is:
Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
get the json file proList like this:
[
{
"id": 1,
"img": "https://gw.alicdn.com/tfs/TB1wx_RQFXXXXbGXFXXXXXXXXXX-250-125.png_200x200q85s150.jpg_.webp",
"desc": "英国证券交易所,国家:英国, 适合人群:本科毕业生 专科毕业生,时间:2019.01.08-2019.02.08",
"tag": "hot"
},
{
"id": 2,
"img": "https://gw.alicdn.com/tfs/TB1wx_RQFXXXXbGXFXXXXXXXXXX-250-125.png_200x200q85s150.jpg_.webp",
"desc": "美国白宫,国家:美国, 适合人群:美籍华人 老外, 时间:2019.01.08-2019.02.08",
"tag": "quality"
}
]
Now I think I know, The Project component get an object but render it incorrect,
finially the project component like this:
class Project extends PureComponent {
constructor(props) {
super(props);
console.log('projcet component props', props);
}
render() {
let { target } = this.props;
console.log('target', target);
return (
<ProjectWrap>
<div>{target.desc}</div>
</ProjectWrap>
)
}
}
I am trying to get an item "icon" from "weather" form following JSON
{
"coord": {
"lon": 14.33,
"lat": 49.94
},
"weather": [{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}]
}
I can't figure out how to exctract an item which is in array through render method.
My code is:
class Weather extends React.Component {
constructor() {
super();
this.state = {
'items': []
}
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch('http://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4')
.then(results => results.json())
.then(results => this.setState ({'items': results}));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>
})}
</div>
);
}
}
I actually used this question here: Get access to array in JSON by ReactJS ...which got me this far, but still can't make it working...
Your weather array is not set until your fetch request is complete, so this.state.items.weather.map in your render method will result in an error.
You could give weather an empty array as default value.
class Weather extends React.Component {
constructor() {
super();
this.state = {
items: {
weather: []
}
};
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch(
"http://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4"
)
.then(results => results.json())
.then(results => this.setState({ items: results }));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>;
})}
</div>
);
}
}
copy paste this example in codesandbox.io .you were initializing the items in constructor as array(where as fetch gave you an object) and for the initial render, items.weather was undefined and in render method you were trying to access map of undefined which was causing the error. (I have changed the url to https to run it in codesandbox)
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = {
items: {}
};
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch(
"https://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4"
)
.then(results => results.json())
.then(results => this.setState({ items: results }));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather &&
this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>;
})}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I'm trying to render a Table, but I'm getting a render error, I looked up other stack's questions and there was suggested that I should use map for returning object array values. I also used render inside map. My object looks like this:
[
{
amount_left: "100",
category: "vegtable",
food_name: "potatos",
price: "1",
type: "salty"
},
{
amount_left: "100",
category: "cheese",
food_name: "cheese",
price: "0.5",
type: "salty"
},
...
]
My code.
import React, { Component } from 'react';
import { Table } from 'reactstrap';
class Meals extends Component {
getMeals = async () =>{
const api_call = await fetch(`http://127.0.0.1/RFIDSys/rfid_handler.class.php?action=getAllMeals`);
const data = await api_call.json();
console.log(data[0].food_name) // returns potatos
return data.map((item,i) => {
return (<tr><td>{item.food_name}</td></tr>)
})
}
render(){
return (
<div>
<Table>
<tbody>
{this.getMeals()}
</tbody>
</Table>
</div>
);
}
}
export default Meals;
Cant see what's wrong, I'm getting "Objects are not valid as a React child (found: [object Promise]).If you meant to render a collection of children, use an array instead." error.
The error that suggest that use array instead, ain't I using arrays in map function or it's still an object what I'm returning?
Your render function is synchronous function. However, getMeals function is asynchronous function.
Async-await keyword wraps you function into promise, so getMeals function return a promise to your render function, consequently you can't use getMeals in render function.
You can solve your task by using state:
import React, { Component } from "react";
import { Table } from "reactstrap";
class Meals extends Component {
state = { meals: null };
componentDidMount() {
this.loadMeals();
}
loadMeals = async () => {
const api_call = await fetch(
`http://127.0.0.1/RFIDSys/rfid_handler.class.php?action=getAllMeals`
);
const data = await api_call.json();
console.log(data[0].food_name);
this.setState({ meals: data });
};
render() {
if (!this.state.meals) {
return null;
}
return (
<div>
<Table>
<tbody>
{this.state.meals.map((item, i) => (
<tr>
<td>{item.food_name}</td>
</tr>
))}
</tbody>
</Table>
</div>
);
}
}
export default Meals;