Getting undefined error while setting a state - javascript

I am using API to extract data from Amazon API and I am using react js for it.
The problem is when I extract them and save it in the state it gives me an error of undefined, however, I am getting the result from the API. The function takes time to get data from API, I am using a method of extracting data which is suitable in node js but also working in react and I am getting a result, but unable to save it in a state. Here's the code below:
let getReport={
path:'/',
query:{
Action:'GetReport',
ReportId:'14941942615018036',
Version:'2009-01-01',
}
};
let temp=null;
mws.request(getReport, function(e, result) {
temp=result;
this.setState({amazon_data:result}) // gives undefined error
});
I think the reason is the function execution takes time and it runs the setstate line which makes the result null, any suggestions on how to fix this?

That happens because this.setState is not part of the this scope inside your function.
let getReport={
path:'/',
query:{
Action:'GetReport',
ReportId:'14941942615018036',
Version:'2009-01-01',
}
};
let temp=null;
const _this = this;
mws.request(getReport, function(e, result) {
temp=result;
_this.setState({amazon_data:result}) // this works because _this references the outer this
});
That should work or you can use arrow functions like below.
let getReport={
path:'/',
query:{
Action:'GetReport',
ReportId:'14941942615018036',
Version:'2009-01-01',
}
};
let temp=null;
mws.request(getReport, (e, result) => {
temp=result;
this.setState({amazon_data:result}) // this works because the arrow function binds the outer this to the function
});

Related

Dynamically get data from firebase realtime database during runtime in javascript

I have the following problem: I want to get data from a specific node from firebase during runtime. It should display "stats" of a player that was selected before. Now I could use on() to get all the data in the beginning, but I want to save data transfers by only downloading the data of on player if I need to, so I tried to use once like this:
var firebaseRef = firebase.database().ref();
function getScoresOfPlayer(player) {
console.log(player);
var selectedPlayerScores = [];
firebaseRef.once('value').then(function(snap) {
snap.child('scores').child('thierschi').forEach(function(child) {
selectedPlayerScores.push([child.key, child.val()]);
});
});
return selectedPlayerScores;
}
The problem is that it retruns the array before the data was loaded into it. Also I checked the docs and didn't find a better solution.
Thanks in advance!
This is because the getScoresOfPlayer function returns selectedPlayerScores before the promise returned by the once() method resolves.
You should include the return within the then(), as follows:
var firebaseRef = firebase.database().ref();
function getScoresOfPlayer(player) {
console.log(player);
var selectedPlayerScores = [];
return firebaseRef.once('value') //return here as well
.then(function(snap) {
snap.child('scores').child(player).forEach(function(child) { //I guess it should be child(player) and not child('thierschi') here
selectedPlayerScores.push([child.key, child.val()]);
});
return selectedPlayerScores;
});
}
which means that you have to call your function as follows, since it is going to be asynchronous and to return a promise:
getScoresOfPlayer('xyz')
.then(function(selectedPlayerScores) {
....
})

Inserting into Collection after Promises in a Meteor Method

I'm using this Gumroad-API npm package in order to fetch data from an external service (Gumroad). Unfortunately, it seems to use a .then() construct which can get a little unwieldy as you will find out below:
This is my meteor method:
Meteor.methods({
fetchGumroadData: () => {
const Gumroad = Meteor.npmRequire('gumroad-api');
let gumroad = new Gumroad({ token: Meteor.settings.gumroadAccessKey });
let before = "2099-12-04";
let after = "2014-12-04";
let page = 1;
let sales = [];
// Recursively defined to continue fetching the next page if it exists
let doThisAfterResponse = (response) => {
sales.push(response.sales);
if (response.next_page_url) {
page = page + 1;
gumroad.listSales(after, before, page).then(doThisAfterResponse);
} else {
let finalArray = R.unnest(sales);
console.log('result array length: ' + finalArray.length);
Meteor.call('insertSales', finalArray);
console.log('FINISHED');
}
}
gumroad.listSales(after, before, page).then(doThisAfterResponse); // run
}
});
Since the NPM package exposes the Gumorad API using something like this:
gumroad.listSales(after, before, page).then(callback)
I decided to do it recursively in order to grab all pages of data.
Let me try to re-cap what is happening here:
The journey starts on the last line of the code shown above.
The initial page is fetched, and doThisAfterResponse() is run for the first time.
We first dump the returned data into our sales array, and then we check if the response has given us a link to the next page (as an indication as to whether or not we're on the final page).
If so, we increment our page count and we make the API call again with the same function to handle the response again.
If not, this means we're at our final page. Now it's time to format the data using R.unnest and finally insert the finalArray of data into our database.
But a funny thing happens here. The entire execution halts at the Meteor.call() and I don't even get an error output to the server logs.
I even tried switching out the Meteor.call() for a simple: Sales.insert({text: 'testing'}) but the exact same behaviour is observed.
What I really need to do is to fetch the information and then store it into the database on the server. How can I make that happen?
EDIT: Please also see this other (much more simplified) SO question I made:
Calling a Meteor Method inside a Promise Callback [Halting w/o Error]
I ended up ditching the NPM package and writing my own API call. I could never figure out how to make my call inside the .then(). Here's the code:
fetchGumroadData: () => {
let sales = [];
const fetchData = (page = 1) => {
let options = {
data: {
access_token: Meteor.settings.gumroadAccessKey,
before: '2099-12-04',
after: '2014-12-04',
page: page,
}
};
HTTP.call('GET', 'https://api.gumroad.com/v2/sales', options, (err,res) => {
if (err) { // API call failed
console.log(err);
throw err;
} else { // API call successful
sales.push(...res.data.sales);
res.data.next_page_url ? fetchData(page + 1) : Meteor.call('addSalesFromAPI', sales);
}
});
};
fetchData(); // run the function to fetch data recursively
}

How to use getMapping function of Elasticsearch JS

I am trying to use this getMapping function seen here in the api. I am trying to get the mapping for an index in my database. So far I've tried this
var indexMap = client.indices.getMapping(['indexName'], function() {
console.log(indexMap);
});
and
var indexMap = client.indices.getMapping({index: 'indexName'}, function() {
console.log(indexMap);
});
both tries fail and log { abort: [Function: abortRequest] }
So I took a closer look at the ElasticSearch JS Quick Start docs to see how they were using the methods. I was confused by the API because I thought it was supposed to take an array client.indices.getMapping([params, [callback]]). But I now understand that it takes an object with params inside that object and then returns the response in a callback. The function does not return anything relevant as far as I can tell. Heres the code I used to get the mapping on 'myIndex' index. The mapping is stored in the response object.
Code:
client.indices.getMapping({index: 'patents'}, function(error, response) {
if (error) {
console.log(error);
} else {
console.log(response);
}
});

RangeError: Maximum call stack size exceeded from unknown reason

A little intro: I actually got the problem fixed as of writing this, now I would just want to know, why it was there in the first place:
I'm trying to retrieve data from mongoDB with mongoose. The code works without problems 99% and for some reason only finding data from certain table WITH certain function causes "Maximum call stack size exceeded"-error.
Now I have jasmine tests which should confirm that the datalayerdatas-table works fine, but when it retrieves the data through getData.js, it throws the error.
Code in stack-order.
gameData-spec.js (jasmine spec):
getData.getDataLayer(dataToFind)
.then(function(data) {
console.log("DATA getDataLayer", data[0]);
})
.catch(function(e) {
console.log("Error in finding dataLayerData", e.stack);
});
getData.js (proxy / API functionality to get the data):
ret.getDataLayer = function getData(data) {
console.log(666)
return getData(dataLayerData.getDataByPlayer, {gameID: data.gameID, playerID: data.playerID, turnID: data.turnID});
};
function getData (DBfunc, data) {
var promise = DBfunc(data);
return promise;
}
gameData.js (retrieves data from mongoDB):
dataLayerData: (function dataLayerData () {
var ret = {};
ret.insert = function (data) {
return insertData( models.dataLayerData, data);
};
ret.getDataByPlayer = function (data) {
var ObjectId = require('mongoose').Types.ObjectId;
return getData( models.dataLayerData, { gameID: ObjectId(String(data.gameID)), playerID: data.playerID, turnID: data.turnID } );
};
return ret;
})()
For the other tables I didn't even need the mong.Schema.Types.ObjectId-specification, when finding data, but I tried it with this one, didn't have any effect though (basically I need it when manually doing mongoDB from command line so I assume I would need it here too).
Also the mongoDB Schema:
var dataLayerData = new mong.Schema({
"playerID": Number,
"gameID": mong.Schema.Types.ObjectId,
"turnID": Number,
"objects": [
{
"dataName": String,
"dataType": String,
"objects": {
}
}
]
});
Now as said, there are other tables being retrieved exactly the same way and they work. Even if I replace the gameData-spec.js.call with some other database-find, it works without problems. I do not see why it even gets into a callback-loop and how.
For debugging and clearing the problem I set the console.log(666) to getData.js and it keeps spamming the console for a while. So for some reason it seems that function gets called over and over.
Later on I noticed that the getData call was unnecessary (that is a stripped down version). Why does just that call in between cause a maximum call stack error?
The problem is that getData inside getDataLayer is not the getData function defined below, is actually the getData function callee above, used for recursion. Recursion can cause overflow in JavaScript.
// fixed ---v---
ret.getDataLayer = function(data) {
console.log(666)
return getData(dataLayerData.getDataByPlayer, {gameID: data.gameID, playerID: data.playerID, turnID: data.turnID});
};

Setting a result returned from rest api to a template variable in Meteor

I am learning how to use Meteor and I am trying to connect to google map api and return json
using meteor.http.get.The following code works fine and i can set the template variable test equal to the json returned and view it(i want to use this for learning purposes for now):
if (Meteor.isServer) {
Meteor.methods({
getGoogleMaps: function () {
this.unblock();
return Meteor.http.call("GET", "http://maps.googleapis.com/maps/api/geocode/json",
{params:{address:"8-10 Broadway, London SW1H 0BG,United Kingdom",
sensor:false}});
}
});
}
if (Meteor.isClient) {
Template.main.test=function(){ return Session.get("response");}
Meteor.call("getGoogleMaps", function(error, results) {
Session.set("response", results.content);
});
}
But the following methods to assign the json returned to the test template variable do not work:
if (Meteor.isClient) {
var response;
Meteor.call("getResponses", function(error, results) {
response= results.content;
});
Template.main.test=function(){ return response;}
}
This does not work either:
if (Meteor.isClient) {
Meteor.call("getResponses", function(error, results) {
Template.main.test= results.content;
});
}
Why do the last two methods do not work? What would be the most appropriate method to set a template variable from a result returned from a rest api?
The second method sets the value, but by the time the response is received from the server, the client has already rendered the template so you aren't seeing the result. This kind of timing problem is a common issue when first getting started with asynchronous javascript and its one of the main reasons that Meteor's reactivity is so appealing.
The third method has the same timing issue as the second but it also is setting a template helper to a non-function value so that is invalid.
The first method works as you'd expect due to Meteor's reactivity. This line:
Template.main.test=function(){ return Session.get("response");}
...registers a dependency on Session.get('response'). When the response is finally received from the server, the call to Session.set('response') triggers a recomputation of all dependencies so the template gets rendered again with the received value.
You can see this more explicitly by doing something like this:
if (Meteor.isClient) {
Template.main.rendered = function () {
console.log('[main] rendered');
};
Template.main.helpers({
test: function () {
console.log("[main] 'test' helper executed");
return Session.get("response");
}
});
Meteor.call("getGoogleMaps", function(error, results) {
console.log("[main] response received");
Session.set("response", results.content);
});
}

Categories