Parsing JSON from ReactJS application - javascript

I am trying to parse a nested json request from my reactjs web app.
Below is the json that I received from a request.
response.data
{
"total": 2,
"offset": 1,
"limit": 987,
"staging": [
{
"id": 101,
"name": "Test Stage"
},
{
"id": 102,
"name": "Dev Stage"
},
{
"id": 103,
"name": "Prod Stage"
}
]
}
I need to parse “staging” and display the results on browser screen.
Below is the code that I am trying to parse. But, it is throwing error (SyntaxError: Unexpected token o in JSON at position 1).
export default class ItemLister extends React.Component {
state = {
persons: []
}
componentDidMount() {
axios
.get('https://xxx.yyy.zzz/xyz/zyx/', {
headers: {
'authorization':'Bearer XXXXXXXXX',
'X-Api-Key': 'XXXXXXXXXXXXXX',
},
withCredentials: true
})
.then(response => {
console.log(response.data) // it gets the correct response and printing in logs
const persons = response.data;
this.setState({ persons });
})
.catch (err => {
console.log("error")
});
}
render() {
return <ul>{this.state.persons.map(person => <li>{person.name}</li>)}</ul>
}
}
ReactDOM.render(<ItemLister />, document.getElementById('root'))
registerServiceWorker()
I couldn't find fix for it. Can someone guide me whether the parsing of such json is correct or not and how to get the parsed results and displayed on screen?

An error occurs because you're trying to parse an Object instead of a String. Simply skip JSON.parse and set result to response.data:
.then(response => {
console.log(response.data) // it gets the correct response and printing in logs
this.setState({ result: response.data });
})
And in you render:
render() {
return (
<ul>
{ this.state.result &&
this.state.result.staging &&
this.state.result.staging.map(person => <li>{person.name}</li>)
}
</ul>
);
}

Related

parsing HashMap failed, expected Object, but encountered Array

For creating an action at hasura I'm using the following node.js code (still at an experimental stage) in glitch.com -
const execute = async (gql_query, variables) => {
const fetchResponse = await fetch(
"https://example.com/v1/graphql",
{
method: "POST",
body: JSON.stringify({
query: gql_query,
variables: variables
})
}
);
// console.log('DEBUG: ', fetchResponse);
const data = await fetchResponse.json();
console.log("DEBUG: ", data);
return data;
};
// paste the code from codegen here
const ACTION_INSERT_PAYSLIP_GET_DRIVER_PAYMENT_DATA = `
query getDriverPaymentData ($orders: [Int!]!) {
company_order (where: {company_order_id: {_in: $orders}}) {
company_order_details (distinct_on: stage_cost_driver_id) {
stage_cost_driver_id
company_user {
delivery_salary
}
}
}
}`
// Request Handler
app.post('/action_insert_payslip', async (req, res) => {
// get request input
const { order_list } = req.body.input
console.log('Input', order_list)
const orders = order_list.order_id
console.log('Item: ', orders)
const { data:driverPaymentData, errors:driverPaymentError} = await execute(ACTION_INSERT_PAYSLIP_GET_DRIVER_PAYMENT_DATA, orders)
console.log('Driver Payment Data: ', driverPaymentData)
// run some business logic
// success
return res.json({
// payslip_list: "<value>"
payslip_list: order_list
})
});
The query getDriverPaymentData produces an output like the following in hasura api explorer:
{
"data": {
"company_order": [
{
"company_order_details": [
{
"stage_cost_driver_id": 1,
"company_user": {
"delivery_salary": 20
}
},
{
"stage_cost_driver_id": 6,
"company_user": {
"delivery_salary": 10
}
}
]
},
{
"company_order_details": [
{
"stage_cost_driver_id": 6,
"company_user": {
"delivery_salary": 10
}
}
]
}
]
}
}
But in the log, I'm getting the following output:
Input { order_id: [ 247, 260, 253 ] }
Item: [ 247, 260, 253 ]
DEBUG: { errors:
[ { extensions: [Object],
message:
'parsing HashMap failed, expected Object, but encountered Array' } ] }
Driver Payment Data: undefined
It says that it expects object but encountered array. But from what I see, I'm already getting an object "data": {[....]} with array inside it from the output at hasura's API console.
What am I missing here? How can I get the data of stage_cost_driver_id and delivery_salary?
Shouldn't variables be an object?
body: JSON.stringify({
query: gql_query,
variables: {orders: variables}
})

TypeError: this.state.dashboarddata.map is not a function

I am unable to map the object inside the array, it is always giving an error. Not sure why it is happening.
I have checked with array as well but no luck.
Below is my App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
dashboarddata: [],
}
}
componentDidMount() {
fetch('http://www.mocky.io/v2/5d8f41e63200004d00adebcd', {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then((response) => response.json())
.then(dashboarddata => this.setState({ dashboarddata }));
}
render() {
const { dashboarddata } = this.state;
//console.log(dashboarddata);
return (
<div>
<div className="campaign-status">
{this.state.dashboarddata.map((dynamicData) =>
<div>{dynamicData.total_subscribers}</div>
)}
<CampaignStatus status = {this.state.dashboarddata} />
</div>
</div>
);
}
}
export default App;
Below is my Json Format
{
"total_subscribers": 250000,
"total_campaign": 400,
"page_info": [
{
"total_pages": 20,
"total_items": 387
}
],
"subscriber_data": [
{
"sub_name": "User1",
"email": "user1#gmail.com",
"contest_id": "115",
"createdAt": 1569628118967,
"phone": 971582384662,
"country": "bahrain",
"gender": "female"
},
{
"sub_name": "User2",
"email": "user2#gmail.com",
"contest_id": "115",
"createdAt": 1569628118967,
"phone": 97158231245454,
"country": "kuwait",
"gender": "male"
}
]
}
I want to use HOC so need to pass the same data to another components as well.
Map not work on object it work on Array. Please make
componentDidMount() {
fetch('http://www.mocky.io/v2/5d8f41e63200004d00adebcd', {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then((response) => response.json())
.then(dashboarddata => this.setState({ dashboarddata: [dashboarddata] }));
}
In javascript, map is function of the array. If you want to print result use indexing for get and print data
Try This ..
let data = this.state.dashboarddata;
for (var key in data) {
if (data.hasOwnProperty(key)) {
console.log(key); // key
console.log(data[key]); // value
}
}
First of all please use 'https' instead of 'http' for the url. And try using the code mentioned by #Ashok. He is right you cannot iterate through an object with map you to typecast response to an Array.

Angular Http Call map Api Response

I'd like to get my trips, which are my response from API in Angular.
From the backend I'm getting:
{
"trips": [
{
"id": 0,
"name": "string",
"startDate": "2019-06-30T06:05:48.006Z",
"endDate": "2019-06-30T06:05:48.006Z",
"description": "string",
"roomSharing": true,
"countries": [
{
"id": 0,
"name": "string",
"code": "string"
}
],
"languages": [
{
"id": 0,
"name": "string",
"code": "string"
}
]
}
]
}
which is fine, but I have a problem on the client side.
Here's my code for getting trips:
getTrips(): Observable<Trip[]> {
return this.http.get<Trip[]>(this.apiUrl + '/Trip/Get')
.pipe(
tap(_ => console.log('fetched trips')),
retry(1),
catchError(this.handleError),
map(data => {
return data;
})
);
}
and in my component I have:
loadTrips() {
return this.rest.getTrips()
.subscribe((data) => {
this.trips = data;
console.log(this.trips);
}
, err => console.log(err));
}
I'd like to get trips in a template like:
<div class="card mb-3 trip" *ngFor="let trip of trips">
but I have to like:
<div class="card mb-3 trip" *ngFor="let trip of trips.trips">
So, the question is how can I map my response to get Trip array instead of Array of Trips array?
Unless I'm misunderstanding something, this should work:
interface TripsResponse {
trips: Trips[],
}
getTrips(): Observable<Trip[]> {
// use your response interface instead
//return this.http.get<Trip[]>(this.apiUrl + '/Trip/Get')
return this.http.get<TripsResponse>(this.apiUrl + '/Trip/Get')
.pipe(
tap(_ => console.log('fetched trips')),
retry(1),
catchError(this.handleError),
map(data => {
return data.trips; // set it properly here
})
);
}
Change your return statement:
return this.http.get('/Trip/Get')
.pipe(
tap(_ => console.log('fetched trips')),
retry(1),
catchError(this.handleError),
map((data: TripsResponse) => { // change made here; make data of type TripsResponse
return data.trips;
})
);
where TripsResponse is
interface TripsResponse {
trips: Trips[],
... // other fields for future if required
}
Dont over complicate by doing .map, just do:
loadTrips() {
return this.rest.getTrips()
.subscribe((data) => {
this.trips = data.trips;
}
, err => console.log(err));
}
Also, correct the model Trip[] which you have created it should be
export interface ITripsResponse {
trips: Trips[],
}
return this.http.get<ITripsResponse>(this.apiUrl + '/Trip/Get')
or else, correct .map by
map((data) => {
return data.trips;
})
and then Observable<Trip[]> would be a valid return type

Is it possible to get values for <options> from an endpoint that sends back json response using axios?

So, I have this endpoint: http://127.0.0.1:8000/api/materials
that would return this json response:
{
"data": [
{
"uuid": "05a36470-d0a0-11e7-91b4-ff3d7d9f961a",
"title": "Apple",
"viewing_time": 15,
"description": "",
"organization_id": null,
"created_at": "2017-11-24 06:45:36",
"updated_at": "2017-11-24 06:45:36",
"deleted_at": null
},
{
"uuid": "2048f730-bfa0-11e7-95fb-6dceb95ba437",
"title": "Banana",
"viewing_time": 15,
"description": "It's a fruit",
"organization_id": null,
"created_at": "2017-11-02 15:33:31",
"updated_at": "2017-11-02 15:33:31",
"deleted_at": null
},
{
"uuid": "3b6a1020-d0a0-11e7-b6bb-d77fc76d610b",
"title": "Strawberry",
"viewing_time": 15,
"description": "",
"organization_id": null,
"created_at": "2017-11-24 06:47:06",
"updated_at": "2017-11-24 06:47:06",
"deleted_at": null,
},
I want to pick all the titles and make them options.
and this is my function where axios is called:
materialList = () => {
var token = localStorage.getItem('jwt');
var apiBaseUrl = "http://127.0.0.1:8000/api/materials";
var config = {
headers: {
'Authorization': "bearer " + token,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
withCredentials: false
}
axios.get(apiBaseUrl, config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
and this is where I want the titles (Apple, Banana and Strawberry) to appear:
<Form.Input list='material' placeholder='Material' name="material_id" id="material_id" onChange={this.onChange}/>
<datalist id='material_id'>
<option value=/** What do I put here **/ />
</datalist>
I have used axios when submitting a post request to the api, but can I trigger an axios get request as soon as the page loads, so that I can get the title I needed ?
First create a state variable as shown below in your component.
constructor(props) {
super(props);
this.state = {
options: []
}
}
Now, you can use componentDidMount() to get those values from the API as shown below.
componentDidMount() {
const token = localStorage.getItem('jwt');
const apiBaseUrl = "http://127.0.0.1:8000/api/materials";
const config = {
headers: {
'Authorization': "bearer " + token,
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}
axios.get(apiBaseUrl, config)
.then((response) => {
this.setState({
options: response.data
})
})
.catch((error) => {
console.log(error);
});
}
Now you can use that state variable to show in the option.
render() {
const { options } = this.state;
return(
<Form.Input list='material' placeholder='Material' name="material_id" id="material_id" onChange={this.onChange}>
{options.map((item, index) => <option key={index} value={item.uuid}>{item.title}</option>)}
</Form.Input>
)
}
First, add an options array to your state.
Next, in your axios function:
axios.get(apiBaseUrl, config)
.then(response => {
this.setState({
options: response.data.map(item => item.title),
});
})
Finally, in your UI component (assuming you've made the options from earlier available as a variable of the same name):
const optionList = options.map(option => <option value={option} />)
render() {
return (
// Other JSX here..
<datalist id='material_id'>
{optionList}
</datalist>
)
}
I assume that the jsx code you post is what inside your component render function.
If you required data from external source and want to make a http request to get these data whenever the component is mounted. What you might want to do is to get the data inside componentDidMount, save it to your state, and use it inside your render function, an example can be found below:
class YourComponent {
// Use componentDidMount to get the data via axios
componentDidMount() {
// ... Your code to prepare the axios call
/*
* Use arrow function to keep refer to React component `this`
* and sae the response data to the component state
*/
axios.get(apiBaseUrl, config)
.then(
response => this.setState({options: response.data})
)
.catch(function (error) {
// handle the error here
});
}
render() {
// Options will have the same format as your response data
const { options } = this.state;
return (<datalist id='material_id'>
{options.map(option =>
<option value={/* can be any attribute you want from the result object, like id, title, ..etc*/}>
{option.title}
</option>)}
</datalist>);
}
}
Regarding the triggering of the API request when the page will load:
Make yourself familiar with the React lifecycle methods.
https://reactjs.org/docs/react-component.html
In this case I would go for the componentDidMount() method:
componentDidMount() {
this.materialList();
}
If you do not plan to use redux to save the state, you need will probably need to call a setState() here in order to save the result of your request in your component's state (like Nico described).

React map nested array within object

The goal is to pull in the nested array "records". My current output displays the array within the react console, but with an error. I will try and be as concise as possible but will keep things short.
The error screen has 3 lines that are referencing _getRecords so im positive that _getRecords is the problem child.
class RecordBox extends Component {
constructor() {
super();
this.state = {
showComments: false,
results: []
};
}
render(){
const records = this._getRecords();
return (
// jsx template code...
);
}
// API Call
_fetchRecords() {
$.ajax({
method: 'GET',
url: 'http://apidata:8888/data.json',
success: (results) => {
this.setState({ results });
},
error: () => {
console.log('error');
}
});
}
_getRecords() {
// TypeError: this.state.results.map is not a function...
return this.state.results.map((record) => {
return <Comment body={record["Contractor Name"]} />
});
}
}
I have a feed that looks like the below. I do not have permission to modify this.
{
"result": {
"records": [
{
"id": 1,
"email": "clu.hey#gmail.com",
"author": "Clu",
"Contractor Name": "Just say no to love!!",
"avatarUrl": "https://placeimg.com/200/240/any"
},{
"id": 2,
"email": "hello#gmail.com",
"author": "Anne Droid",
"Contractor Name": "I wanna know what love is...",
"avatarUrl": "https://placeimg.com/200/240/any"
}
]
}
}
I think you just aren't setting the state to the right thing. Your state.results is currently an object. Just make sure when you set your state, you set state.results to results.result.records
this.setState({ results: results.result.records })
One thing you could also do is map the results directly in the jsx code and skip using the _getRecords function.
<div>
{
this.state.results.map( ( record ) => {
return <Comment />
}
}
</div>
This is the way I usually write this as it's easier for me to read, but it's personal preference.
I hope this helps!
The _fetchRecords function needs to change to:-
_fetchRecords() {
$.ajax({
method: 'GET',
url: 'http://apidata:8888/data.json',
success: (results) => {
this.setState({ results: results.result.records });
},
error: () => {
console.log('error');
}
});
}

Categories