I have the following object that gets returned from an API fetch
{
id: 1,
name: "Leanne Graham",
zipcode: "92998-3874",
username: "Bret",
email: "Sincere#april.biz",
..... (only the ones above matter to me)
}
I want to extract only certain parts of the objects ( e.g. zipcode ). I tried the following: this.props.users.zipcode JSON.parse JSON.stringify but all of them return undefined when logging to console. Do you mind helping me?
Considering only your data the below possible solution will help you in get the values of the keys.
import React, {Component} from 'react';
export default class Test extends Component {
render(){
let data = [{"id":1,"name":"Leanne Graham","username":"Bret","email":"Sincere#april.biz","address":{"street":"Kulas Light","suite":"Apt. 556","city":"Gwenborough","zipcode":"92998-3874","geo":{"lat":"-37.3159","lng":"81.1496"}},"phone":"1-770-736-8031 x56442","website":"hildegard.org","company":{"name":"Romaguera-Crona","catchPhrase":"Multi-layered client-server neural-net","bs":"harness real-time e-markets"}}]
data.map((d, i) => {
console.log("values", d.name, d.email, d.address.zipcode )
});
return(
<div>
</div>
)
}
}
or
import React, {Component} from 'react';
export default class Test extends Component {
render(){
if(this.props.users && this.props.users.map((d, i) => {
console.log("values", d.name, d.email, d.address.zipcode )
});
return(
<div>
</div>
)
}
}
I read from your comment, that you are using Redux.
The result of an API-fetch should be an action. In the associated reducer, you need to retrieve the payload of the action triggered by the successful request.
As others have pointed out before, it would be easier to answer and not just fire shots in the dark if you provided a code-example
Related
I want to get a price of Bitcoin from this JSON array.
I tried several approaches with map() function, although without success. Why does the error say it's TypeError: Cannot read property 'map' of undefined? I saw a similar question but the answers there do not work for me.
import logo from './logo.svg';
import React from 'react'
import './App.css';
import BoxComponent from './BoxComponent.js';
import Footer from './Footer.js'
import Header from './Header.js'
class App extends React.Component {
constructor(){
super()
this.state = {
loading: false,
data: []
}
}
componentDidMount() {
this.setState({loading: true})
fetch("https://api.coindesk.com/v1/bpi/currentprice.json")
.then(response => response.json())
.then(data => {
this.setState({
data: data,
loading: false
})
})
}
render(){
const price = this.state.loading ? "Loading.. :)" : this.data.map(price => <div>{price.bpi.gbp}</div>)
return (
<div className="App">
<Header />
{price}
<Footer />
</div>
);
}
}
export default App;
This is a JSON file:
{"time":{"updated":"Feb 4, 2021 20:38:00 UTC","updatedISO":"2021-02-04T20:38:00+00:00","updateduk":"Feb 4, 2021 at 20:38 GMT"},"disclaimer":"This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org","chartName":"Bitcoin","bpi":{"USD":{"code":"USD","symbol":"$","rate":"37,498.3099","description":"United States Dollar","rate_float":37498.3099},"GBP":{"code":"GBP","symbol":"£","rate":"27,422.0266","description":"British Pound Sterling","rate_float":27422.0266},"EUR":{"code":"EUR","symbol":"€","rate":"31,337.0376","description":"Euro","rate_float":31337.0376}}}
You're applying .map on something that's not an array. I would recommend reading a bit more on what map is for. MDN is a good starting point.
The first problem is that on the first render, loading will be false and your data will be undefined, so it will fail to display it. You could do this instead in your constructor:
this.state = {
loading: true,
data: undefined
}
Your render function has several issues:
First, to access state you should use this.state.data instead of this.data.
Then looking at the JSON file you've linked, the structure of your data looks like this:
{
bpi: {USD: {…}, GBP: {…}, EUR: {…}},
chartName: "Bitcoin",
disclaimer: "This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org",
time: {updated: "Feb 4, 2021 21:17:00 UTC", updatedISO: "2021-02-04T21:17:00+00:00", updateduk: "Feb 4, 2021 at 21:17 GMT"}
}
The path you're using for accessing the data doesn't match with this structure. I'll assume that you're looking for bpi.GBP.rate (but it could also be bpi.GBP.rate_float), in which case your render function should be:
render(){
const price = this.state.loading ? "Loading.. :)" : (<div>{this.state.data.bpi.GBP.rate_float}</div>)
return (
<div className="App">
<Header />
{price}
<Footer />
</div>
);
}
Also, I wouldn't keep a string for "Loading" in the price variable, but that's a different topic.
Here you have a working CodeSandbox: https://codesandbox.io/s/zealous-breeze-qwvbb (I have omitted Header and Footer for obvious reasons).
I would need some help to fetch some data in a local file calling data.json to my React component. The data is very simple, but when i tried to connect with my component, all I have in the component appear less than the information I added from the data file.
this is my data.json:
{ "data": [
{ "id": "1",
"name": "john"
},
]}
...and this is my component where i need to fetch the data and where everything is working less than the information I want to connect and appear completely blank.
This is the function where i past the information in the first instant to send the information to the state.
function RenderFoo({data, name}) {
return (
<div>{data.name}</div>
)}
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
data : [data]
}}
render() {
const dataExample = this.state.data.map((element) => {
return (
<div key={element.url}>
<RenderFoo data ={ element }/>
</div>
)})
return (
<div>
<Card >
{dataExample}
</Card>
</div>)
The screen appear blank in the part of the component that I connect the data but without any error in the other part of the component where everything is working. I think the sintaxis to get the information is not right any reason don't read the data.
And if I change data.name in the function is giving error. I don't know if I'm missing the key or so.
Moving all the data to the main component is worthy neither because I will need to increase the data after and I will thousands of lines, and create a complete back end would be pointless for this kind of application
Thanks
Your state has a property data which is an array. Each element of that array is an object with properties id and name -- and maybe url?
So then what are the props supposed to be here:
function RenderFoo({data, name}) {
return (
<div>{data.name}</div>
)}
Does RenderFoo take a single property data which is the the whole data object? Or does it take the properties of data as individual props? Either is fine, but it feels like you are mixing the two. So remove name from the props.
<div key={element.url}>
Do all elements in your data have a url property? I'm only asking because your sample just shows name and id.
this.state = {
data : [data]
}}
This also looks suspect to me. You are taking the variable data and making it a single element in an array. I'm not sure exactly what your data variable looks like, but I think you probably want to set it as the entire state, this.state = data.
Try this:
import React, { Component } from "react";
import json from "./data";
function RenderFoo({ data }) {
return <div>{data.name}</div>;
}
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
data: json.data
};
}
render() {
return (
<div>
{this.state.data.map((element) => (
<div key={element.id}>
<RenderFoo data={element} />
</div>
))}
</div>
);
}
}
I removed the <Card> component because I don't know where you imported it from, but you can easily add it back it.
I’m working with an API that shows data for cryptocurrencies called CryptoCompare. I’m a React noob but I’ve managed to use Axios to do the AJAX request. However I’m having trouble accessing the JSON elements I want.
Here’s what the JSON looks like: https://min-api.cryptocompare.com/data/all/coinlist
Here is my request:
import React, { Component } from 'react';
import './App.css';
import axios from "axios";
var NumberFormat = require('react-number-format');
class App extends Component {
constructor(props) {
super(props);
this.state = {
coinList: []
};
}
componentDidMount() {
axios.get(`https://min-api.cryptocompare.com/data/all/coinlist`)
.then(res => {
const coins = res.data;
//console.log(coins);
this.setState({ coinList: coins});
});
}
// Object.keys is used to map through the data. Can't map through the data without this because the data is not an array. Map can only be used on arrays.
render() {
console.log(this.state.coinList.Data);
return (
<div className="App">
{Object.keys(this.state.coinList).map((key) => (
<div className="container">
<span className="left">{key}</span>
<span className="right"><NumberFormat value={this.state.coinList[key].CoinName} displayType={'text'} decimalPrecision={2} thousandSeparator={true} prefix={'$'} /></span>
</div>
))}
</div>
);
}
}
export default App;
I am able to output some JSON using console.log(this.state.coinList.Data);. It outputs the JSON object, but I am unable to console.log properties of the object itself.
How would I, for example, output the CoinName property of the first element 42?
console.log(this.state.coinList.Data.CoinName) doesn’t work
nor does console.log(this.state.coinList.Data[0].CoinName) etc…
You are iterating over this.state.coinList while you want to iterate over this.state.coinList.Data.
Try this:
render() {
const data = this.state.coinList.Data;
if (data == null) return null;
return (
<div className="App">
{Object.keys(data).map((key) => (
<div className="container">
<span className="left">{key}</span>
<span className="right"><NumberFormat value={data[key].CoinName} displayType={'text'} decimalPrecision={2} thousandSeparator={true} prefix={'$'} /></span>
</div>
))}
</div>
);
}
CodeSandbox here: https://codesandbox.io/s/3rvy94myl1
I also stumbled with that same problem as yours. You cannot access an object inside a data because it is empty when the render happens. What I did was I made a conditional render where if the data is empty, it will just show a loading screen or something like that. And when the data loads , it will access the object inside that data. I can now access the object inside because I waited for the data to load inside render.
I hope this answer can help future react users
return (
<div>
{this.state.coinList.length>0? <h1>{this.state.coinList[0].coinName}</h1>: "Loading"}
</div>
);
}
Added: To console.log the data , you can create a new component inside the conditional render. Inside that component, you can access all the data you want because it is rendered after the data is loaded.
You're might have to parse the JSON. Might be good to do that before you save it.
const coins = JSON.parse(res.data)
I'm working on my first complicated React app and I am making a request to a movie API. My site allows the user to do a search in a searchbar for whatever movie, show, actor, etc... that they are searching for. I'm pulling the user's search query and inserting it into an api request like this:
export const getDetails = (id) => {
return new Promise(function(resolve, reject) {
axios.get(`https://api.themoviedb.org/3/movie/` + id +`?api_key=&language=en-US`)
.then(function(response) {
resolve(response)
})
.catch(function(error) {
reject(error)
})
})
}
I'm able to get the data like this and console.log it:
import React, { Component } from 'react';
import Header from '../header';
import {Link} from 'react-router-dom';
import axios from 'axios';
import Footer from '../Footer.js';
import Searchbar from '../header/searchbar.js';
import List from '../results/list';
import {getDetails} from '../api/getDetails';
class Detail extends Component {
constructor(props) {
super(props);
this.state = {
id: this.props.match.params.id,
result: null,
error: false,
}
}
componentWillMount() {
getDetails(this.state.id).then(function(response){
this.setState({result: response});
console.log(response.data.original_title);
console.log(response.data.homepage);
console.log(response.data.popularity);
console.log(response.data.release_data);
console.log(response.data.overview);
}.bind(this)).catch(function(err) {
this.setState({
result:"There was a problem loading the results. Please try again.",
error: true
})
}.bind(this))
}
render() {
return(
<div>
<Header/>
<div className="details-container">
<h2>Details: </h2>
</div>
</div>
)
}
}
export default Detail
Console.logging it in the componentWillMount function successfully logs the data but I am not able to access the data in the render function via something like {response.data.orginal_title). How would I render the data being logged in componentWillMount?
TLDR; You can access your state variables from within your render function via this.state. Something like: console.log(this.state.result.data.origin_title) outside of the jsx and {this.state.response.data.orginal_title} inside the jsx.
P.S. You are using the correct this.
The following are picky recommendations and explanations, feel free to disregard.
It's recommended to make requests for data in componentDidMount. That can be read here in the docs for componentDidMount.
You're using arrow functions already in your get details function, if you convert the rest of your functions to arrow functions you no longer have to explicitly bind this to each one; it's automatically set be the this of it's parent. See the "No Separate This" section in the MDN docs
If you don't need any of the header information I would save response.data into your state so you don't have to type as much when you want to access the data. this.state.result.original_title vs this.state.result.data.original_title. That's just me and I'm lazy.
axios does return a promise like Eric said so you don't actually need to wrap it in the extra promise. You can just straight up return it and since arrow functions automatically return one line expressions you can spiff that up into a one liner:
export const getDetails = id => axios.get(`https://api.themoviedb.org/3/movie/${id}?api_key=&language=en-US`)
Finally you should be able to access the data you've stored in your state from your render function as mentioned in #3 above. Outside of the JSX you can console.log it like normal console.log(this.state.result), inside your JSX, however, you will need to make sure you escape with {} like: <div>{this.result.original_title}</div>
Small working example here: https://codesandbox.io/s/zqz6vpmrw3
You can simply use
{this.state.result}
inside the render.
I'm working on an Angular 2 application, and I'm trying to use JSON data, either local/mocked or fetched via HTTP, and display it on a component. I have an injectable service that will do the fetching/mocking -
import { Injectable } from 'angular2/core';
#Injectable()
export class TestService {
testString:string = "";
testDetails: string = "";
constructor() { }
getTestDetails(): Promise<string> {
this.testDetails = {
"status": "success",
"message": "Data save successful",
"data": {
"Random_Data_1": "Random Data 1",
"Random_Data_2": "Random Data 2"
}
};
return Promise.resolve(JSON.stringify(this.propertyDetails));
}
}
And then I have a component that uses the service via Dependency Injection -
import { Component, OnInit } from 'angular2/core';
import {TestService} from "./test.service";
#Component({
selector: 'test',
templateUrl: './test.component.html',
styleUrls: []
})
export class TestComponent implements OnInit {
testDetails: string = "";
constructor(private testService: TestService) { }
ngOnInit() {
this.display();
}
display(): void {
this.testService.getTestDetails()
.then(
testDetails => {
this.testDetails = JSON.parse(testDetails);
},
errorMessage => {
console.error("Something failed trying to get test details");
console.error(errorMessage);
}
);
}
}
The component HTML -
<div class="content">
<p> Test Details </p>
<p> {{ testDetails.data.Random_Data_1 }} </p>
</div>
The problem is, the HTML is erroring out trying to display the items in the testDetails JSON. I initially used it with md-tabs, so the first try would error out, but the other tabs would read the data fine. Also, the ngOnInit would be called twice when the error occurs. I have narrowed it down to the data coming in and the object types that is causing me the headache.
I know I can create a Details class and declare testDetails of type Details, and then map the JSON data into the class, but the thing is, I want to work with generic data, and only know a few components that will be present in the data. Is there a way to read the JSON, and use the data without having to define a separate class for each scenario ?
I have a plunker with the most basic stuff set up. The actual setup runs fine on my local system up until where I try to access the JSON data in the HTML, at which point the browser throws a cryptic error. The skeleton code doesn't even run on Plunker. That said, the structure in the Plunker defines the structure of my app and the data flow. Plunker with the basic setup
What is the best way to achieve this ? What is the standard/best practice to do this ?
Throwing another option out there, since you asked about best way to achieve this. Might not be the best idea, this is subjective ;) But if I were you...
Thinking about the future, where you will use real backend, it could be nice to use mock json file. If/when you move over to a real backend, you wouldn't basically need to change anything else but the url of the requests :)
So I set up a simple example for you. Here I used Observables, but you can use Promises if you prefer that. Here's more info on HTTP if you want/need to read up on that. Most important thing is that you have the HttpModule imported in your app module.
You have your file with JSON and in your service make http-requests to that:
getTestDetails() {
return this.http.get('src/data.json')
.map(res => res.json())
}
Your display-method:
display() {
this.testService.getTestDetails()
.subscribe(data => {
this.testDetails = data;
});
}
And in the template use the safe navigation operator to safeguard null/undefined values:
<div class="content">
<p> Test Details </p>
<p> {{ testDetails?.data?.Random_Data_1 }} </p>
</div>
Here's a
Demo
As said, this is to give another approach on how to implement the things you want to achieve, and this would probably be my preferred way :)
Use
<p *ngIF="testDetails.data.Random_Data_1 "> {{ testDetails.data.Random_Data_1 }} </p>
This is because there is no data initially.Hope this helps you.