Fetch requests in ReactJS - javascript

I am working in a small application for a class I am taking and I have an issue when I am using the fetch API
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
map: "",
markers: [],
Data: []
};
}
componentDidMount() {
fetch(
`https://api.foursquare.com/v2/venues/explore?near=ashkelon&v=20180729&client_id=MVLZGLPIAITJITM0OOFYER3C2ZRT5ERGGEWCC0T1YWV3HFZA&client_secret=1TBLTY0TSM1T320FEO3BJBGTMYVQPCMBOGO5GEBC0ZB1E5LK`
)
.then(function(response) {
return response.json();
})
.then(
function(data) {
this.setState({ Data: data });
}.bind(this)
)
.catch(function(e) {
console.log("There is an issue with getting the information", e);
});
}
}
window.initMap = this.initMap;
loadJS("https://maps.googleapis.com/maps/api/js?key=AIzaSyDySeqpuOjQlckWJUMlbSW_w5CydOVTWJI&callback=initMap");
UPDATE :
this will not provide with an error and the state is set, but what now happens is that my state is empty when i log the state in the initMap method.
At this point i see that the state is set for "that".
But if its set for "that" how can i use "this" state in the rest of my application i need this information to create markers on the google maps API
thanks in advance.

The problem is that this is undefined in your anonymous function. By assigning const that = this, you make the context from componentDidMount() available in all of the anonymous functions. Another solution is to bind() all functions with the correct context. For example
...
.then((function(data) {
this.setState({Data: data.response.groups[0]})
console.log(this.state.Data);
}).bind(this))
...
Now you can remove the declaration for that.

If you don't really care about IE11 support (and does not use Babel) please consider using arrow functions (it's syntax sugar for functions that have the same this as the this around them)
Note that string literals like you used, have similar compatibility table as arrow functions so you lose nothing and gain much cleaner code!
The code with arrow functions looks like this:
componentDidMount() {
/* ... */
fetch(`URL`)
.then(response => response.json())
.then(data => this.setState({Data: data.response.groups[0]}))
.catch(e => console.log('There is an issue with getting the information' , e))
/* ... */
}

Related

How to assign response object to state

I could get data from nodejs backend to react frontend using axios. But I can't assign that object to state object in React.
getData=()=>{
let responseToHandle;
axios.get("http://localhost:9000/api/getData")
.then(function (response)
{
console.log(response.data);//This is working
responseToHandle = response.data;
console.log(responseToHandle);//This is working
this.setState({
data: responseToHandle}, () => {
console.log(this.state.data)
})
})
// HERE IS MY STATE OBJECT
state={
data:[]
}
axios call and this.setState are both asynchronous.
You must add your desired code inside a callback:
this.setState({
data: responseToHandle
}, () => {
console.log(this.state.data)
// Now you can use this.state.data
});
Edit:
You also need to change
axios.get("http://localhost:9000/api/getData").then(function (response) { ... })
to
axios.get("http://localhost:9000/api/getData").then(response => { ... })
Without arrow function the scope inside .then() is the function scope, different from component scope, which means using this gives you a different value.
This code won't work properly:
console.log(this.state.data)
You called a console.log function synchronously right after a promise declaration. Promise works asynchronously.
So in order to check a new state, you should call console.log in render() method, or the best option is to use componentDidUpdate() method.
E.g.
componentDidUpdate(prevProps, prevState) {
console.log(`Previous data: ${prevState.data}`);
console.log(`New data: ${this.state.data}`);
}

JS React: error of this.x is not a function, even when this.x function is bound

I'm trying to update to update the window state whenever the App component mounts. With the below code, I receive an Error in response to tabs.query: TypeError: this.addTabs is not a function.
I don't understand why this.addTabs is not considered a function, as the function is above the reference to this.addTabs(tabs), and I think it was correctly bound.
class App extends Component {
constructor(props){
super(props);
this.state = {
window: []
};
this.addTabs = this.addTabs.bind(this);
}
addTabs(tabs){
this.setState({window:this.state.window.concat(tabs)});
};
componentDidMount(){
chrome.tabs.query({active:true},function(tabs){
this.addTabs(tabs);
});
I'm not looking to use the arrow function. I looked at similar questions, and the response was to bind the function in the constructor, which I believe I did. Any help or pointers would be appreciated!
Your issue is in this block:
componentDidMount(){
chrome.tabs.query({active:true},function(tabs){
this.addTabs(tabs);
});
}
Inside your callback, the context is different so this refers to another context.
You have several ways to fix it.
1) Assign the ref to this outside the callback and use that ref:
componentDidMount(){
const that = this;
chrome.tabs.query({active:true},function(tabs){
that.addTabs(tabs);
});
}
2) Bind the current this to the callback:
componentDidMount(){
chrome.tabs.query({active:true},(function(tabs){
this.addTabs(tabs);
}).bind(this));
}
3) Use an arrow function:
componentDidMount(){
chrome.tabs.query({active:true}, (tabs) => {
this.addTabs(tabs);
});
}

Double rendering in React with asynchronous call in componentDidMount causing error

I'm building a blog application that has an articles index page, and from there you can click on an article to see the article or edit the article.
If you're going from the index page to the edit page, it works just fine because I already have all the articles in state. But if I refresh after I've gone to the edit-article page, I no longer have all the articles in state.
This is a problem because I'm making an asynchronous recieveSingleArticle call in the componentDidMount of my edit-article page, then I setState so my form is prepopulated. There's a double render which causes an "Uncaught TypeError: Cannot read property 'title' of undefined" error, presumably during the first render before the article has been received into state.
class ArticleEdit extends React.Component {
constructor(props) {
super(props);
this.state = {title: "", body: "", imageFile: ""};
this.handleChange = this.handleChange.bind(this);
this.handlePublish = this.handlePublish.bind(this);
this.handleFile = this.handleFile.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
componentDidMount() {
const { article, requestSingleArticle } = this.props;
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}
...
I tried wrapping my async calls inside of an "if (this.props.article)" but that didn't work. Is there a best way of dealing with this type of problem? Any advice greatly appreciated!
UPDATE:
Another solution that works is to have a componentDidUpdate in addition to componentDidMount. check in componentDidMount if this.props.article exists and if so, setState. And in componentDidUpdate, wrap the setState in the following conditional:
if (!prevProps.article && this.props.article)
Just check if the article is present in the props before calling async action
componentDidMount() {
const { article, requestSingleArticle } = this.props;
if (!(article && requestSingleArticle)) return; // this line
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}
Since you are not getting any render from this method , it means that the props are not yet obtained in the life cycle method componnetDidMount. So instead you can use componentWillReceiveProps like this
componentWillReceiveProps(nextProp) {
// this line here will check the article props' status so that
// we will not use setState each time we get a prop
if (this.props.article === nextProp.article) return;
// rest is just the same code from above
const { article, requestSingleArticle } = nextProp;
if (!(article && requestSingleArticle)) return; // this line
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}

Consuming REST resources before rendering in React.js

I have React components that consumes external webservice to reach data for rendering.
I would like the data being loaded BEFORE the rendering because I want it to be indexed by search engines.
Here is my component :
class AboutPage extends React.Component {
async componentWillMount() {
let response = await EventWS.doSearch();
this.setState({
eventList : response.data
})
}
render() {
/* ... */
}
}
(I tried to use async/await because I thought it could help, but no).
When trying to load the page with server side rendering I got the warning :
Warning: setState(...): Can only update a mounting component. This usually means you called setState() outside componentWillMount() on the server. This is a no-op. Please check the code for the FluxContainer(AboutPage) component.
Indicating that the setState is done AFTER the componentWillMount has ended.
In my specific case, what is best way to acheving this ? Is there a easy way to do an ajax call synchronously ? Is it recommended to do so ?
Thank you.
EDIT :
I have found a library that allow to make synchronous call :
https://github.com/ForbesLindesay/sync-request
But it states that it is not well suited for production. So I'am a bit disapointed.
I dont have a lot of experience with Flux but it seems like you should do this in the componentDidMount method.
You can use Axios promise based get in componentDidMount to implement it, for an example you can refer Handling Events In React and below sample code:
constructor(props) {
super(props);
this.state = {eventList: []};
this.Axios = axios.create();
}
componentDidMount() {
let _this = this;
this.Axios.get('/your/rest/url')
.then(function (response) {
console.log(response);
_this.setState({eventList: response.data});
}).catch(function (error) {
console.log(error);
});
}
Or if you are already using Axios then make sure EventWS.doSearch() return Promise to you and call it in componentDidMount, as follows:
constructor(props) {
super(props);
this.state = {eventList: []};
}
componentDidMount() {
let _this = this;
EventWS.doSearch().then(function (response) {
console.log(response);
_this.setState({eventList: response.data});
}).catch(function (error) {
console.log(error);
});
}

when to use this.getInitialView = this.getInitialView.bind(this);

I am following Getting started with React Native and Firebase tutorial on medium.
I came across below code.
constructor(props) {
super(props);
Firebase.initialise();
this.getInitialView();
this.state = {
userLoaded: false,
initialView: null
};
//What this code does?
this.getInitialView = this.getInitialView.bind(this);
}
getInitialView() {
firebase.auth().onAuthStateChanged((user) => {
let initialView = user ? "Home" : "Login";
this.setState({
userLoaded: true,
initialView: initialView
})
});
}
can some body explain below statement? when and why should i use it? and what are all the advantages?
this.getInitialView = this.getInitialView.bind(this);
Basically anytime you want to use this, you want it to be referring to the component. ES6 Classes don't autobind, so there are multiple ways to handle this issue:
You can bind in the constructor like in your example.
You can use an arrow function wherever you call your method: ( ) => this.getInitialView( ).
You can bring in a library to autobind the methods for you.
I do it this way: When defining the methods, do it like this, since arrow functions do not define a scope of their own:
getInitialView = () => {
firebase.auth().onAuthStateChanged((user) => {
let initialView = user ? "Home" : "Login";
this.setState({
userLoaded: true,
initialView: initialView
})
});
}
It bind the this keyword to the react component. Otherwise you couldn't use this inside the method.
The difference is without the line you question this would refer to something else and not have setState as a function which is important to react components.
It does appear though, given the context, that it's not used because the only usage of this is in another function it should change the context. I'd think you'd need to do to achieve the desired results. (code snippet removed)

Categories

Resources