fetch JSON (http get) not working - javascript

I want run it using http get, but it not show nothing, Where is the error?. Angular http.get easier to get JSON and doing ngFor and show, but on React is little special. So, in conclusion I don't like do a simple "import data from './data.json'", I need load json from the cloud.
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class App extends Component {
// 1.JSON
constructor(props) {
super(props);
this.state = {
items: [],
};
}
// 2. JSON
componentJSON() {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(data => this.setState({ items: data.items }))
}
render() {
// this.componentJSON = this.componentJSON.bind(this);
this.setState({ items: data})
// 3. JSON
// const { items } = this.state;
return (
<Router>
<div className="App">
<ul>
{items.map(item =>
<li key={item.title}>
{item.title}
</li>
)}
</ul>
</div>
</Router>
);
}
}
export default App;

Working now!,
Thanks anyway friends!
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
items : []
};
// You should bind this object to componentWillMount method, other setState was not working
this.componentWillMount = this.componentWillMount.bind(this);
}
// This method is call before component will mounted
componentWillMount() {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then( data => this.setState({ items : data }) );
}
render() {
const { items } = this.state;
return (
<ul>
{items.map(item =>
<li key={item.title}>
{item.title}
</li>
)}
</ul>
);
}
}
export default App;

In your function you are assuming that the context is the class, but its not, its not how js works, so when you are trying to use this.setState it would not work because the context of the function doesnt have any function called setState.
A simple way of solving this is binding the function to the class, by simply adding the following line in the ctor:
this.componentJSON = this.componentJSON.bind(this);

You need to call your componentJSON function.
It is best to do this within componentDidMount()
componentDidMount(){
this.componentJSON()
}
This will get called when the component is rendered in the browser.
It is a common mistake to call your API within componentWillMount() but this will make your API call happen twice.
As mentioned in my other comment, be careful about calling your API in your render function as it will mean that you API is called every time there is a re-render. A re-render happens after setting state and since you are setting state in your API call, it will cause an infinite loop

Use componentDidMount
componentDidMount ()
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(data => this.setState({ hits: data.hits }))
}

Related

React isn't recognizing object within my state

So i'm building a component in react that is fetching data from an API, and I'm getting a build error in browser. it's telling me that down in the render section, It's not recognizing "students". But i've looked at how the JSON data from the API is structured, and it follows that. I set the state to the data of the JSON, yet when I render it doesn't recognize students? Any tips? thank you so much!
import React from "react";
import axios from "axios";
export default class FetchRandomUser extends React.Component {
state = {
students: []
}
componentDidMount() {
axios.get("...")
.then(data => {
this.setState({ students: data})
console.log(this.state)
})
}
render(){
return (
<ul>
{ students.map(student =>
<li key={student.id}>
{student.city}
</li>
)
}
</ul>
)
}
}
You need to access students from state property of the component,
render(){
const {students} = this.state;
return (
<ul>
{ students.map(student =>
<li key={student.id}>
{student.city}
</li>
)
}
</ul>
)
}
Extending #majdsalloum
setState is async so if you immediately console.log state it won't reflect in your state students variable
try with your state in a constructor like
constructor(props) { super(props); this.state = { students: []}; }
add this.state in front of your students variables.
also, you can view your axios response by just using
console.log(data)

Using ComponentDidMount to load data before page renders - React

I am using ComponentDidMount to call data from my database and render page when data is ready. However, i have noticed the speed of my application has reduced when navigating since i have to wait for the data.
This is happening when i have large data in the database i am retrieving. My question is, is there any way of optimizing this, or i just have to render page before data loads ?
Component.JS
componentDidMount()
{
this.fetchAllItems();
}
fetchAllItems(){
return this.fetchPost().then(([response,json]) => {
console.log('here now ',response);
console.log(localStorage.getItem('user_token'))
if(response.status === 200)
{
}
})
}
fetchPost(){
const URL = 'http://localhost:8000/api/';
return fetch(URL, {method:'GET',headers:new Headers ({
'Accept': 'application/json',
'Content-Type': 'application/json',
})})
.then(response => Promise.all([response, response.json()]));
}
Try to use axios to make call to API asynchronously, after it's done, just update your response data to state. No need to wait your page is finished loading or not, react will render by following changes of state value.
import React from 'react';
import axios from 'axios';
export default class MovieList extends React.Component {
state = {
movies: []
}
componentDidMount() {
axios.get(`http://localhost/movies`)
.then(res => {
const movies = res.data;
this.setState({ movies: movies });
})
}
render() {
const {
movies
} = this.state;
return (
<div>
<ul>
{ movies.map(movie => <li>{movie.name}</li>)}
</ul>
</div>
)
}
}
Have you tried the shouldComponentUpdate lifecycle method? This method accepts nextProps (new or upcoming props) and nextState (new or upcoming State) parameters. You can compare your next props and state (state preferably in your case) to determine if your component should re-render or not. Fewer re-renders equals to better speed and optimization. that means your pages will load faster. The shouldComponentUpdate method returns a boolean to determine if a page should re-render or not. Read more here. Also, Here's an example:
class App extends React.Component {
constructor() {
super();
this.state = {
value: true,
countOfClicks: 0
};
this.pickRandom = this.pickRandom.bind(this);
}
pickRandom() {
this.setState({
value: Math.random() > 0.5, // randomly picks true or false
countOfClicks: this.state.countOfClicks + 1
});
}
// comment out the below to re-render on every click
shouldComponentUpdate(nextProps, nextState) {
return this.state.value != nextState.value;
}
render() {
return (
<div>
shouldComponentUpdate demo
<p><b>{this.state.value.toString()}</b></p>
<p>Count of clicks: <b>{this.state.countOfClicks}</b></p>
<button onClick={this.pickRandom}>
Click to randomly select: true or false
</button>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
In your case all the optimization must be done in the backend.
But if there is something that can be done in React is using Should Component Update as previous comment mentioned.

fetch Data when context changes in React

I need to make a new api request to fetch data for a given dataId.
this value lives in the Context.
import { MyContext } from './Context'
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = {
dataId: this.context.state.dataId // tried setting state first but didn´t work.
}
this.details = this.details.bind(this)
}
details() {
fetch('https://api.mydomain.com/' + this.context.state.dataId)
.then(response => response.json())
.then(data => this.setState({ data: data }));
}
componentDidMount() {
this.details()
}
render() {
return(
<MyContext.Consumer>
{(context) => (
<div>data: {JSON.stringify(data)} dataId: {context.state.dataId}</div>
)}
</MyContext.Consumer>
)
}
}
MyComponent.contextType = MyContext;
export default MyComponent
from others components I can set new values like
this.context.setDataId(1)
and this will show up correctly but the problem is that is not making a new fetch to get new data for the dataId that changed in the Context.
not sure what´s the correct lifecycle method I can use to detect changes in the context and make a new call to this.details()
I didn´t add the Context code here because it works fine. but if you need to see it please let me know.
In react, you must use life cycle hooks to inspect data such as props or context, to know if the state needs to update for your component. The most common life cycle hook for this purpose is componentDidUpdate(). it gives you the ability to decide whether or not your component needs to update state/rerender based on changes in props that caused the component to update. the following should work for your use case:
import { MyContext } from './Context'
class MyComponent extends Component {
state = {
data:[],
dataId:null
}
details = () => {
// we only want to update if dataId actually changed.
if(this.context.state.dataId !== this.state.dataId){
this.setState({dataId:this.context.state.dataId});
fetch('https://api.mydomain.com/' + this.context.state.dataId)
.then(response => response.json())
.then(data => this.setState({ data: data }));
}
}
componentDidMount() {
this.details()
}
componentDidUpdate() {
this.details();
}
render() {
return(
<MyContext.Consumer>
{(context) => (
<div>data: {JSON.stringify(this.state.data)} dataId: {context.state.dataId}</div>
)}
</MyContext.Consumer>
)
}
}
MyComponent.contextType = MyContext;
export default MyComponent;

Rendering fetched data to screen

I built a component which uses Axios get request and retrieves a list of email addresses.
I don't know what should I write inside render() so I will be able to see the emails list over my website.
This is my suggestion:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {Link} from "react-router";
import axios from 'axios';
export class GetEmailsComponent extends Component {
state = {
emails: []
}
componentDidMount(){
//this.setState({emailList : undefined});
axios.get('./api/EmailAddresses')
.then(response => {
this.setState({emails: response.data});
console.log(response.data);
}).catch(function (error) {
console.log(error);
});
}
render() {
return (
<div>
<button type = "button" onClick= {this.state.emails.map(email => <div>{email}</div>)}>GET ALL EMAILS</button>
</div>
);
}
}
When I check the Console I see an array of the desired emails.
I am looking for a suggestion of how to edit my code so it will render all this mails to the screen (After the button clicked).
Thanks is advance.
Inside your render method, you can map over this.state.emails and return an element for each email (At first the array will be empty, so maybe you can add a condition so you wouldn't render an empty div for no reason) Something like:
render() {
return (
<div>
{this.state.emails.map(email => <div>{email}</div>)}
</div>
);
}
As for componentDidMount - It's a lifecycle method of React. Anything you put there will run after the component mounts for the first time. If you want to trigger the call to Axios once the button is clicked, define a different function (like fetchEmails) and call it using this.fetchEmails.
You have used a componentDidMount life cycle in react to fetch the data. And you called that method via a button. Normally we are not calling life cycle methods like this. i think its better to read the react documentation doc for get an idea about life cycles.
You can declare a function and can call that function via a button. Please find below answer.
class App extends Component {
constructor(props) {
super(props);
this.state = {
emails: [],
showEmails:false,
};
}
componentDidMount () {
axios
.get("./api/EmailAddresses")
.then(response => {
this.setState({ emails: response.data });
console.log(response.data);
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
<button type="button" onClick={() => this.setState({showEmail:true})}>
Get all mails
</button>
{this.state.showEmail && this.state.emails.map(email => <div>{email}</div>)}
</div>
);
}
}
Change your code to something like below.
You need to get emails when button is clicked so you need have custom event handler function for that but not componentDidMount method. You cannot call componentDidMount method as event handler function.
Also when you render emails in loop you need to set unique key to top element inside loop. Key can be a index or unique id from data. Looks like you don’t have unique id from emails array so you can use index as key like below
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {Link} from "react-router";
import axios from 'axios';
export class GetEmailsComponent extends Component {
state = {
emails: []
}
getEmails = () =>{
//this.setState({emailList : undefined});
axios.get('./api/EmailAddresses')
.then(response => {
this.setState({emails: response.data});
console.log(response.data);
}).catch(function (error) {
console.log(error);
});
}
render() {
return (
<div>
<ul>
{this.state.emails.map((email, index)=> <li key={"Key-"+index}>{email}</li>)}
</ul>
<button type="button" onClick={()=> this.getEmails()}>Get all mails</button>
</div>
)
}
}

ReactJS: I can not get the state to update with API data when the asynchronous API data fetch is completed

I am having a bit of an issue rendering components before the state is set to the data from a returned asynchronous API request. I have a fetch() method that fires off, returns data from an API, and then sets the state to this data. Here is that block of code that handles this:
class App extends Component {
constructor() {
super();
this.state = {
currentPrice: null,
};
}
componentDidMount() {
const getCurrentPrice = () => {
const url = 'https://api.coindesk.com/v1/bpi/currentprice.json';
fetch(url).then(data => data.json())
.then(currentPrice => {
this.setState = ({
currentPrice: currentPrice.bpi.USD.rate
})
console.log('API CALL', currentPrice.bpi.USD.rate);
}).catch((error) => {
console.log(error);
})
}
getCurrentPrice();
}
You will notice the console.log('API CALL', currentPrice.bpi.USD.rate) that I use to check if the API data is being returned, and it absolutely is. currentPrice.bpi.USD.rate returns an integer (2345.55 for example) right in the console as expected.
Great, so then I assumed that
this.setState = ({ currentPrice: currentPrice.bpi.USD.rate }) should set the state without an issue, since this data was received back successfully.
So I now render the components like so:
render() {
return (
<div>
<NavigationBar />
<PriceOverview data={this.state.currentPrice}/>
</div>
);
}
}
export default App;
With this, I was expecting to be able to access this data in my PriceOverview.js component like so: this.props.data
I have used console.log() to check this.props.data inside my PriceOverview.js component, and I am getting 'null' back as that is the default I set intially. The issue I am having is that the components render before the API fetch has ran it's course and updated the state with the returned data. So when App.js renders the PriceOverview.js component, it only passes currentPrice: null to it, because the asynchronous fetch() has not returned the data prior to rendering.
My confusion lies with this.setState. I have read that React will call render any time this.setState is called. So in my mind, once the fetch() request comes back, it calls this.setState and changes the state to the returned data. This in turn should cause a re-render and the new state data should be available. I would be lying if I didn't say I was confused here. I was assuming that once the fetch() returned, it would update the state with the requested data, and then that would trigger a re-render.
There has to be something obvious that I am missing here, but my inexperience leaves me alone.. cold.. in the dark throws of despair. I don't have an issue working with 'hard coded' data, as I can pass that around just fine without worry of when it returns. For example, if I set the state in App.js to this.state = { currentPrice: [254.55] }, then I can access it in PriceOverview.js via this.props.data with zero issue. It's the async API request that is getting me here, and I am afraid it has gotten the best of me tonight.
Here App.js in full:
import React, { Component } from 'react';
import './components/css/App.css';
import NavigationBar from './components/NavigationBar';
import PriceOverview from './components/PriceOverview';
class App extends Component {
constructor() {
super();
this.state = {
currentPrice: null,
};
}
componentDidMount() {
const getCurrentPrice = () => {
const url = 'https://api.coindesk.com/v1/bpi/currentprice.json';
fetch(url).then(data => data.json())
.then(currentPrice => {
this.setState = ({
currentPrice: currentPrice.bpi.USD.rate
})
console.log('API CALL', currentPrice.bpi);
}).catch((error) => {
console.log(error);
})
}
getCurrentPrice();
}
render() {
return (
<div>
<NavigationBar />
<PriceOverview data={this.state.currentPrice}/>
</div>
);
}
}
export default App;
Here is PriceOverview.js in full:
import React, { Component } from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
class PriceOverview extends Component {
constructor(props) {
super(props);
this.state = {
currentPrice: this.props.data
}
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.state.currentPrice != null ? <div className="price">{this.state.currentPrice}</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}
export default PriceOverview;
Thank you in advance to any help, it's much appreciated.
this.setState ({
currentPrice: currentPrice.bpi.USD.rate
})
Do not put an = in this.setState
Ok First thing, when you're writting code on React the components that hold state are the class base components so ... What I see here is that you're creating two class base components so when you pass down props from your app class component to your PriceOverview wich is another class base component you're essentially doing nothing... Because when your constructor on your PriceOverview get call you're creating a new state on that Component and the previous state ( that's is the one you want to pass down) is being overwritten and that's why you're seem null when you want to display it. So it should work if you just change your PriveOverview component to a function base component ( or a dumb component). So this way when you pass down the state via props, you're displaying the correct state inside of your div. This is how would look like.
import React from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
const PriceOverview = (data) => {
return (
<div className="overviewBar">
<div className="currentPrice panel">
//Im calling data here because that's the name you gave it as ref
//No need to use 'this.props' you only use that to pass down props
{data != null ? <div className="price">
{data}</div> : <div className="price">Loading...</div>
}
</div>
</div>
)
}
}
export default PriceOverview;
Whenever you're writing new components start always with function base components if you component is just returning markup in it and you need to pass some data go to his parent component update it (making the api calls there or setting the state there) and pass down the props you want to render via ref. Read the React docs as much as you can, hope this explanation was useful (my apologies in advance if you don't understand quite well 'cause of my grammar I've to work on that)
The thing is constructor of any JS class is called only once. It is the render method that is called whenever you call this.setState.
So basically you are setting currentPrice to null for once and all in constructor and then accessing it using state so it will always be null.
Better approch would be using props.
You can do something like this in your PriceOverview.js.
import React, { Component } from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
class PriceOverview extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.props.data!= null ? <div className="price">{this.props.data}</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}
export default PriceOverview;
Or you can use react lifecycle method componentWillReceiveProps to update the state of PriceOverview.js
componentWillReceiveProps(nextProps) {
this.setState({
currentPrice:nextProps.data
});
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.state.currentPrice != null ? <div className="price">{this.state.currentPrice }</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}

Categories