forEach not working for response.data in React - javascript

I am trying to loop through a response from a data file however, I am getting an error I can't debug or see why it doesn't work. I had tried the forEach and map within a function but it failed just the same.
I had tried the forEach and map within a function but it failed just the same.
class GetTiles extends Component {
constructor(props) {
super(props);
this.state = {
tileData: {},
dataLoaded: false
};
}
loadData() {
axios("assets/data/home.json")
.then(response => {
this.setState({
tileData: response.data.titles,
dataLoaded: true
});
console.log("Local Tiles Ajax Call: Success");
})
.catch(err => {
console.log("Local Tiles Ajax Call", err);
});
}
componentDidMount() {
this.loadData();
}
render() {
return (
<>
<Tile title="1" />
{this.state.tileData.map((item, index) => (
<p key={index}>Hello!</p>
))}
</>
);
}
}
export default GetTiles;
Please note I know it doesn't make sense why I am doing this, but it is to help me understand and debug the issue/Get something working.
{
"_id": "home",
"titles": [
{
"type": "app",
"title": [
{
"text": "Demo",
"locale": "en-gb"
}
],
"data": {
"app_name": "lorem",
"category": "demo"
}
},
{
"type": "app",
"title": [
{
"text": "Demo 2",
"locale": "en-gb"
}
],
"data": {
"app_name": "ipsum",
"category": "sports"
}
}
]
}
I am wanting to be able to target titles within the JSON to get data from it and print out data for each onto the page.
The Error:
A cross-origin error was thrown. React doesn't have access to the actual error object in development. See *** for more information.

Your initial state tileData is an object. You can't forEach or map over an object but that's what you've asked the code to do. It will try to iterate over that state property on the first render which is probably what that error is.
Change
this.state = { tileData: {} }
to
this.state = { tileData: [] }
and then add a condition to your render to return something else if the array is empty, something like:
render() {
if (!this.state.titleData.length) return <div>No data</div>
return this.state.tileData.map(tile => etc
}

First of all you have to change this.state = {tileData: {}, ...} to this.state = {tileData: [],...}.
Your first lines should look like:
constructor(props) {
super(props);
this.state = { tileData: [], dataLoaded: false };
}

Related

setState() in componentDidMount() - When is the intermediate state REALLY noticeable TO THE USER?

The React doc states that the intermediate state will not be shown to the user, but why in this example it does show "loading...." (the intermediate state in this case)?
You may call setState() immediately in componentDidMount(). It will
trigger an extra rendering, but it will happen before the browser
updates the screen. This guarantees that even though the render() will
be called twice in this case, the user won’t see the intermediate
state.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.userId} - {item.title}
</li>
))}
</ul>
);
}
}
}
When I do not use AJAX calls the intermediate state is not seen ("loading...) for example:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: []
};
}
componentDidMount() {
this.setState({
isLoaded: true,
items: [
{ "id": 1, "name": "Apples", "price": "$2" },
{ "id": 2, "name": "Peaches", "price": "$5" }
]
});
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.id} - {item.name}
</li>
))}
</ul>
);
}
}
}
Because in the first example, you're not calling setState immediately in componentDidMount().
You're calling it from a function the asynchronous fetch() invocation (eventually) calls.

How do I display data from a JSON object using ReactJS?

New programmer here learning ReactJS. I have some code that uses axios to make an HTTP request to get XMLData from a local file. Then on the response, I am using xml-js to turn that XML data into a JSON object. Then, I am taking that jsonObj and saving it to a state using setState().
I have a function renderTableRows() that is supposed to return JSX to display my JSON data on the browser. I destructured the state and try to console log from the renderTableRows() but when I try to access users.result.body I get
"TypeError: Cannot read property 'body' of undefined".
When I do it from the then() within the componentDidMount() I am able to access the data. I have also include an excerpt of the data I am reading at the bottom of the code.
I'd like to iterate using map() through all the row array attributes. Any help would be appreciated.
class Table extends Component {
constructor(props) {
super(props)
this.state = {
users: []
}
}
async componentDidMount() {
axios.get(XMLData, {
"Content-Type": "application/xml; charset=utf-8"
})
.then((response) => {
var jsonObj = convert.xml2js(response.data,{compact:true, spaces: 4});
this.setState({users:jsonObj});
//console.log(this.state.users.result.body.row[0].col[0]);
});
}
renderTableHeader = () => {
return <th>
<td>Division Full Path</td>
<td>Billable Hours</td>
<td>Vacation Hours Only</td>
<td>Total Hours</td>
</th>
}
renderTableRows = () => {
const {users} = this.state
console.log(users.result.body);
return <h1>Hello from table rows</h1>
}
render() {
//const { users } = this.state
return <table>
<thead>
<tr>
{this.renderTableHeader()}
</tr>
</thead>
<tbody>
<tr>
{this.renderTableRows()}
</tr>
</tbody>
</table>
}
"header": {
"col": [
{
"label": {
"_text": "Counter Source Date"
}
},
{
"label": {
"_text": "Employee Id"
}
},
{
"label": {
"_text": "Counter Hours"
}
},
{
"label": {
"_text": " Division Full Path"
}
},
{
"label": {
"_text": " Projects/Equip/Vessels\nBillable"
}
},
{
"label": {
"_text": "Counter Name"
}
}
]
}
"body": {
"row": [
{
"col": [
{
"_text": "01/01/2021"
},
{
"_text": "2183"
},
{
"_text": "8.00"
},
{
"_text": "01 - Fort Lauderdale/Salvage"
},
{
"_text": "No"
},
{
"_text": "Holiday"
}
]
}
]
}
Issue
The initial state doesn't match how it is accessed in renderTableRows.
this.state = {
users: []
}
Here this.state.users is an array, so this.state.users.result is undefined. This is fine until you then attempt to access a body property and the error TypeError: Cannot read property 'body' of undefined is thrown.
A Solution
You can either start with valid initial state:
this.state = {
users: {
result: {}
}
}
Or use a bunch of guard clauses in renderTableRows:
renderTableRows = () => {
const { users } = this.state
console.log(users.result && users.result.body);
return <h1>Hello from table rows</h1>
}
Or use Optional Chaining:
renderTableRows = () => {
const { users } = this.state
console.log(users.result?.body);
return <h1>Hello from table rows</h1>
}
Since you mention wanting to map through the rows the first option isn't what you want. If rendering rows it'll be something like:
renderTableRows = () => {
const {users} = this.state
return users.map(user => (....))
}
Update
I suggest setting your state to jsonObj.result properties, this is so you don't need to access the result property each render, it just shortens the access. Map this.state.users.headerColumns to the header columns and map this.state.rows to each row and additionally map the row columns.
class Table extends Component {
constructor(props) {
super(props);
this.state = {
users: {
headerColumns: [],
rows: [],
}
};
}
async componentDidMount() {
axios
.get(XMLData, {
"Content-Type": "application/xml; charset=utf-8"
})
.then((response) => {
var jsonObj = convert.xml2js(response.data, {
compact: true,
spaces: 4
});
this.setState({ users: {
headerColumns: jsonObj.header.col,
rows: jsonObj.body.row
} });
});
}
renderTableHeader = () => {
const { users: { headerColumns } } = this.state;
return (
<th>
{headerColumns.map(col => (
<td key={col.label._text}>{col.label._text}</td>
))}
<td>Total Hours</td>
</th>
);
};
renderTableRows = () => {
const { users: { rows } } = this.state;
return rows.map((row, index) => {
let computedTotal;
return (
<tr key={index}>
{row.col.map((value, index) => {
// compute time total from row data
return (
<td key={index}>{value}</td>
);
})}
<td>{computedTotal}</td>
</tr>
)});
};
render() {
return (
<table>
<thead>
<tr>{this.renderTableHeader()}</tr>
</thead>
<tbody>
{this.renderTableRows()}
</tbody>
</table>
);
}
}

mapping through array of objects

I am trying to map through the following data and return "Balance" but it keeps telling me that "coding" its undefined.
here its the array
"entry": [
{
"resource": {
"id": "1980438",
"type": {
"coding": [
{
"system": "https://www.medeo-health.com/uploads/1/1/8/1/118121028/s491564155970805143-c3-i1-w640_orig.jpeg",
"code": "25062444",
"display": "Balance"
}
]
},
"manufactureDate": "2017-01-08",
"expirationDate": "2020-01-08",
"owner": {
"reference": "Organization/1980437"
}
},
"search": {
"mode": "match"
}
}, ...
this is what I am trying:
import React, { Component } from 'react';
import Device from './Device/Device';
import axios from 'axios';
class Devices extends Component {
state = {
devices: null
}
componentDidMount() {
axios.get('http://hapi.fhir.org/baseDstu3/Device?organization=1980437&_include=Device:organization&_sort=device-name')
.then(res => {
this.setState({ devices: res.data.entry });
})
.catch(error => {
console.log(error);
})
}
render() {
let devices = <p style={{ textAlign: "left", margin: "0" }}>This practitioner have no devices!</p>;
if (this.state.devices) {
devices = this.state.devices.map(device => {
return (
<Device
key={device.resource.id}
name={device.resource.type.coding[0].display}
/>
)
});
}
return (
<div>
{devices}
</div>
);
};
};
export default Devices;
the id returns well but for name it keeps getting "Cannot read property 'coding' of undefined"
what I am doing wrong?
Got the Issue. You are getting undefined because the last object you are receiving does not contain a type property in it. Please Check
Try Something Like this
{this.state.devices.map(device => {
if (device.resource.type) { //check type property exists first then render
console.log(device.resource.type.coding[0].display);
return (
<p key={device.resource.id}>
{device.resource.type.coding[0].display}
</p>
);
} else return null;
})}

How to update fetched data to view next or previous page - Paginated

I am fetching data from a API,then displaying it to the page. I have achieved that,
Now I want to build a next and previous button to render the next page of information.
One of the data returned is metadata to links that can be attached to the base url. I got the data and updated it in my state as:
articlePages: []
the data is structured as :
"metadata": {
"pagination": {
"next_page": "/api/articles/ios_index?page=2",
"current_page": "/api/articles/ios_index?page=1",
"previous_page": "/api/articles/ios_index?page=0"
}
}
How should I build the functions for previous and next, so that they attach the right string to the base url, then fetch the new data?
Here is the response I receive then I update my state:
Response Format:
"metadata": {
"pagination": {
"next_page": "/api/articles/ios_index?page=2",
"current_page": "/api/articles/ios_index?page=1",
"previous_page": "/api/articles/ios_index?page=0"
}
}
"data" :{
"id": 713,
"url": "https:sample.-sale",
"title": "The World According to Eddie Huang",
"published_at": "2017-08-29T04:00:00.000Z",
"published": true,
"hero": "https://d1qz9pzgo5wm5k./CPG9crJHRqSPKQg9jymd",
"listings": [],
"tag_list": [
"eddie-huang",
"television"
],
"franchise": "The Drop",
"slug": "eddie-huang-interview-sale",
"author": "Lawrence Schlossman",
"content_type": "long",
"position": "feature"
}
Here is a snippet of my code, any help is appreciated :
import React from 'react';
import axios from 'axios';
export default class ArticleApi extends React.Component {
constructor() {
super();
this.state = {
blogs: "",
articlePages: []
}
}
fetchData = () => {
axios.get(`https:sample.sale/api/articles/ios_index`)
.then(res => {
this.setState({
blogs: res.data.data,
blogPages: res.data.metadata
})
})
.catch(error => {
return ('Looks like there was a problem: \n', error);
});
}
componentDidMount() {
this.fetchData()
}
previousPage = () => {
axios.get(`https:sample.sale/api/articles/ios_index${this.state.blogPages.pagination.previous_page}`)
.then(res => {
this.setState({
blogs: res.data.data,
blogPages: res.data.metadata
})
})
.catch(error => {
return (error);
});
}
nextPage = () => {
axios.get(`https:sample.sale/api/articles/ios_index${this.state.blogPages.pagination.next_page}`)
.then(res => {
this.setState({
blogs: res.data.data,
blogPages: res.data.metadata
})
})
.catch(error => {
return (error);
});
}
render() {
let feed = "Loading...";
if (this.state.blogs) {
feed = this.state.blogs.map((ele, idx) => {
return (
<div key={idx} >
<div className="articleContent">
<p><strong>{ele.franchise}</strong></p>
<h1 className="title"> {ele.title}</h1>
</div>
</div>
)
})
}
return (
<div>
<h3 FEED</h3>
{feed}
<button onClick={this.previousPage}>Previous Page</button>
<button onClick={this.nextPage}>Next Page</button>
</div>
)
}
}
At present you are building a strange URL for both the next and previous page functions:
axios.get(`https:sample.sale/api/articles/ios_index${this.state.blogPages.pagination.next_page}`)
// but this.state.blogPages.pagination.next_page is equal to "/api/articles/ios_index?page=2", right?
// So if we replace the variable with its value, your url actually looks something like this:
axios.get('https:sample.sale/api/articles/ios_index/api/articles/ios_index?page=2')
The correct call should look like:
axios.get(`https:sample.sale${this.state.blogPages.pagination.next_page}`)
And similarly for previous page.

React.js component run error.Uncaught TypeError: Cannot read property '0' of undefined

I am trying to create a Fetch component, which should send a POST request return a response content. And meanwhile I create a NameForm component, which generate a data to Fetch component to send request. My problem is when I use this.state.result.queryResults[0], I got a error in chrome like this:
Here is my js code:
import React, {Component} from 'react';
import Request from 'react-http-request';
class Fetch extends React.Component {
constructor() {
super();
}
render() {
return (
<Request
url='http://localhost:8080/path'
method='post'
accept='application/json'
type="application/json"
send={this.props.args}
verbose={true}
>
{
({error, result, loading}) => {
if (loading) {
return <div>loading...</div>;
} else {
return <div>{result.text}</div>;
}
}
}
</Request>
);
}
}
class NameForm extends React.Component {
constructor() {
super();
this.state = {value: '', result: []};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
var content = this.state.value;
var split = content.split(/\s+/);
var queryObject = new Object();
queryObject.law = null;
queryObject.character = null;
queryObject.lawRule = null;
if (split.length == 1) {
queryObject.law = split[0];
}
else if (split.length == 2) {
queryObject.law = split[0];
queryObject.character = split[1];
}
else if (split.length > 2) {
queryObject.law = split[0];
queryObject.character = split[1];
queryObject.lawRule = split[2];
}
var json = JSON.stringify(queryObject);
this.setState({result: (<Fetch args={json}/>)});
event.preventDefault();
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange}/>
</label>
<input type="submit" value="Submit"/>
</form>
<table>
<thead>
<tr>
<th>GraphName</th>
<th>Relation</th>
<th>Content</th>
<th>KeyWord</th>
</tr>
</thead>
<tboy>{this.state.result.queryResults[0]}</tboy>
</table>
</div>
);
}
}
ReactDOM.render(<NameForm/>, document.getElementById('react'))
And my post response is a json like this:
{
"path": {
"law": "MyLaw",
"character": "C1",
"lawRule": null
},
"queryResults": [
{
"graphName": "MyLaw/C1",
"relation": "self",
"content": "I am C1",
"keyword": {
"a": 0.4296310331415849,
"b": 0.22019926949447058,
"c": 0.16514945212085294,
"d": 0.16514945212085294,
}
},
{
"graphName": "MyLaw/C1/C1.1",
"relation": "child",
"content": "I am C1.1",
"keyword": null
},
{
"graphName": "MyLaw/C1/C1.2",
"relation": "child",
"content": "I am C1.2",
"keyword": null
},
{
"graphName": "MyLaw/C1/C1.3",
"relation": "child",
"content": "I am C1.3",
"keyword": null
},
]
}
In the constructor, we defined the state initial value
constructor() {
super();
this.state = {value: '', result: []};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
So, the initial value of result is [].
And in handleSubmit, you will fetch data for result.
handleSubmit(event) {
...
this.setState({result: (<Fetch args={json}/>)});
...
}
So, before submitting and data being fetched, the result is []
So, please change
<tboy>{this.state.result.queryResults[0]}</tboy>
To
<tboy>{this.state.resultthis.state.result.queryResults[0]}</tboy>
It is giving you error because when first time it renders it can not find queryResult until the request hit the server.
Try this:
<tboy>
{
this.state.result.queryResults
?
this.state.result.queryResults[0]
: null
}
</tboy>
At your initial render results state is an empty array and it remains like it till the response from fetch call is received. So
<tbody>{this.state.result.queryResults[0]}</tbody>
fails, as result.queryResults is undefined.
So you need a conditional check.
<tbody>{this.state.result.length > 0?
this.state.result.queryResults &&
this.state.result.queryResults[0]:
null}</tbody>

Categories

Resources