React concurrent API calls - javascript

So, my current code looks something like this:
component:
requestDescriptions(params, bothGender) {
if (bothGender) {
// men request
params.gender = 'men';
this.props.getDescriptions(params);
// women request
params.gender = 'women';
this.props.getDescriptions(params);
} else {
this.props.getDescriptions(params);
}
}
and my action looks like this:
export function getDescriptions(params = { brand: null, category: null, gender: null }) {
return (dispatch) => {
dispatch(requestDescription());
return request
.get(`${getApiUrl()}plp?${QueryString.stringify(params)}`)
.end((err, res) => {
if (err && res.statusCode !== 404) {
...
}
if (res.statusCode === 404) {
// HERE:
console.log(params.gender);
return dispatch(receiveEmptyDescription(params));
}
return dispatch(receiveDescription(res.body));
});
};
}
So up until now, I hadn't noticed the error, but just did when I began testing a scenario where both API calls return a 404.
The error being that having the concurrent calls to the same endpoint is overwriting my params. When the API returns a 200, I could not see the problem. But now when I'm testing returning a 404 I can clearly see the problem.
The problem im getting lies here:
this.props.getDescriptions(params);
params.gender = 'women';
If I check the console log inside my action ( //HERE ) , on both cases params.gender displays 'women' even though the first time around its supposed to be 'men'
I guess this can be fixed using a promise?
Let me know if I'm not clear enough.

The problem isn't really that you're making parallel requests or that you're getting an HTTP2XX or an HTTP4XX.
The reason you are always seeing gender equal to 'women' is that you're using the same object instance for both requests, i.e. params.
What is happening is that you set params.gender to be 'men', fire of the first request, set params.gender to be 'women', fire of the second request.
All the steps above happen synchronously, before any request is completed.
This means that long before any request is finished, params.gender is already equal to 'women'.
Just send a different object to each action.
requestDescriptions(params, bothGender) {
if (bothGender) {
// men request
this.props.getDescriptions({ ...params, gender: 'men' });
// women request
this.props.getDescriptions({ ...params, gender: 'women' });
} else {
this.props.getDescriptions(params);
}
}
Since you're already using Redux, you should be aware that mutation is baaaad 😊

Related

Commit mutation from action in another store via root? Vue2 Nuxt Vuex

This has been beating me up.
I have a vuex store, inside is a folder called "RyansBag" im using to test things.
I have two other folder, Alerts and Inventory. So the folder structure for each of these goes
store> Ryansbag/Alert/...
in my Inventory index.js file, we run a function to add an item to an inventory system.
async addInventory_Catalog({commit}, payload){
try{
const response = await this.$axios.put('Inventory/AddFromCatalogDefault', null, {
params:{
originalUPC: payload.upc,
clientID: payload.clientId,
saleprice: payload.sellPrice,
cost: payload.sellPrice,
Condition: payload.condition.conditionName,
Serial: payload.serialNumber,
Notes: payload.notes,
HoldDays: payload.holdDays,
}
});
console.log(response.data.success)
commit('RyansBag/Alerts/showAlerts', 'You have added a product!', {root: true})
return response.data;
} catch (error) { alert(error); console.log(error); }
},
Here we just pass the item down, and when it's done - commit the changes to our alert which is in store > RyansBag/Alerts.
You can see I tried to call it:
commit('RyansBag/Alerts/showAlerts', 'You have added a product!', {root: true})
My understanding was to simply state the commit is coming from this store as the root state...? But Im not sure if im supposed to register the commit in /Alerts as a global item somehow. ( https://vuex.vuejs.org/guide/modules.html#accessing-global-assets-in-namespaced-modules )
EDIT:::
Edit: There was no commit request in the action. added to post. Now however I get warning to not mutate state outside of state handlers..
Below is the mutation it's requesting to reach inside the alerts.
export const mutations = {
showAlerts(state, message) {
let timeout = 0
if (state.status.showAlert) {
state.status.showAlert = false
timeout = 300
}
setTimeout(() => {
state.status.showAlert = true
state.status.message = message
}, timeout)
},
hideAlerts(state) {
state.status.showAlert = false
},
}
The fix was a) making sure I called commit in the action parameters. and b) not using setTimeout in the mutation, but instead setting a mutation to hide, and using the actions to setTime.

Apply error handling in react google analytics

I am using react-ga (https://www.npmjs.com/package/react-ga) module in my React application, so that i can send events, to google analytics.
I have read the documentation of react-ga and i cant find a way to have a response after i send data to google analytics.
The way i m sending data is like so:
import * ReactGA from 'react-ga'
function sendEvent(category: string, action: string, label: string) {
ReactGA.event({
category,
action,
label,
});
}
function sendPageView(title: string, url: string) {
ReactGA.set({ page: title });
ReactGA.send()
ReactGA.pageview(url);
}
These are just calls to google-analytics, without any feedback, What i m looking for is a function that get a callback OR an async function, that executes the above.
POST request to google-analytics:.
Actually, there is an automatic POST request, each time i m sending an event, which fetches 1 on response.(i dont control the POST request)
The request is like so: Request URL: https://www.google-analytics.com/j/collect?v=1&_v=....
What i try to accomplish, is to catch Errors and send them to Sentry server:.
get errors, in case google analytics server is not responding
get error, in case there is a timeout, on the response of google-analytics.com.
Possibly this module is restricted on functionality, has anyone else used something similar and managed to get any kind of Errors ?
thanks.
I solved the case, based on the official google-analytics documentation: https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#hitCallback
I attach the solutions, for reference:
Send an event and call the callback function when completed:
ReactGA.ga('send', 'event', 'Page view' {
hitCallback: (res: any) => {
console.log('*** page view event success.');
return true;
},
});
Send an event and time it to 1sec.
If the callback is not executed by then, i raise a timeout error, otherwise i execute the success code:
let alreadyCalled: boolean = false;
function myCode(timeout: boolean, hasResponded: boolean) {
if (alreadyCalled) return;
alreadyCalled = true;
if (timeout === true && hasResponded === false) {
// TODO, THROW AN ERROR AND SEND TO SENTRY
console.log('Time out error, sending message to Sentry..');
} else if (timeout === false && hasResponded === true) {
// TODO, send event success
console.log('send event to google-analytics, success');
}
}
ReactGA.ga('send', 'event', 'pageview', { hitCallback: () => myCode(false, true) });
setTimeout(() => myCode(true, false), 10);
I hope it helps anyone that needs something similar.

framework7 component page not working with real database query

In framework7 (latest version) there are some sample pages for e.g. page-loader-component.html. This page having -
<p>Hello {{name}}</p>
and at bottom, there is script
return {
data: function(){
return{
name: "Peter"
}
}
}
Now when the page is accessed, it displays - Hello Peter
Question is I want to fetch name from real database from my server. So I made this changes -
app.request.post(
'http://domain-name/page.php',
{userid: 2},
function(response){
var response = JSON.parse(response);
console.log(response); //console log shows {name: "Peter"}
return response
}
);
return {
data: function(){
return response //console log shows response is not defined
}
}
Now when try to access the page, it throws errors (in console) - ReferenceError: response is not defined. In console my request query is OK, it show - {name: "Peter"}
I did return response as well as tried replacing the position of function as well as tried many other possible fix suggested on stackoverflow.
I think one function is running before other one make finish database queries. I am not expert (just average). So please someone suggest.
I have also tried to access the page through routes.js as example given in request-and-load.html but still reference error.
return response is inside the data: section. The request is not, and they cannot reach each other.
Put the gathering of data inside the data function. You also want to save the response outside of the request function. To make sure the response variable is reachable. I'd also personally move the request itself to be defined in a separate location for usage outside of this one instances.
File: custom.js
requests = {
GetName: function () {
app.request.post(
'http://domain-name/page.php',
{ userid: 2 },
function (response) {
var response = JSON.parse(response);
console.log(response); //console log shows {name: "Peter"}
return response
}
);
},
GetNameDynamic: function (id) {
app.request.post(
'http://domain-name/page.php',
{ userid: id},
function (response) {
var response = JSON.parse(response);
console.log(response);
return response
}
);
}
}
Then inside the data: section call that function and save as a variable. Pass that in the data return.
data: function () {
// Must return an object
var result = requests.GetName();
return {
name: result.name,
}
},
There are other ways/locations to accomplish this. One being the async in the route as the other user mentioned.
In the routes array, just change the path and componentUrl to the correct ones.
{
path: '/post-entity-group/:type/:group/:public/',
async: function (routeTo, routeFrom, resolve, reject) {
var result = requests.GetName();
resolve(
{
componentUrl: './pages/post-entity.html',
},
{
context: {
name: result.name,
}
}
)
}
},
I think you have to pass by async routeto load page context (c.f. F7 doc)
You will be able to load datas via resolve callback
Maybe an example can help : async data for page

Ember Understand execution flow between route/controller

I have a "box" route/controller as below;
export default Ember.Controller.extend({
initialized: false,
type: 'P',
status: 'done',
layouts: null,
toggleFltr: null,
gridVals: Ember.computed.alias('model.gridParas'),
gridParas: Ember.computed('myServerPars', function() {
this.set('gridVals.serverParas', this.get('myServerPars'));
this.filterCols();
if (!this.get('initialized')) {
this.toggleProperty('initialized');
} else {
Ember.run.scheduleOnce('afterRender', this, this.refreshBox);
}
return this.get('gridVals');
}),
filterCols: function()
{
this.set('gridVals.layout', this.get('layouts')[this.get('type')]);
},
myServerPars: function() {
// Code to set serverParas
return serverParas;
}.property('type', 'status', 'toggleFltr'),
refreshBox: function(){
// Code to trigger refresh grid
}
});
My route looks like;
export default Ember.Route.extend({
selectedRows: '',
selectedCount: 0,
rawResponse: {},
model: function() {
var compObj = {};
compObj.gridParas = this.get('gridParas');
return compObj;
},
activate: function() {
var self = this;
self.layouts = {};
var someData = {attr1:"I"};
var promise = this.doPost(someData, '/myService1', false); // Sync request (Is there some way I can make this work using "async")
promise.then(function(response) {
// Code to use response & set self.layouts
self.controllerFor(self.routeName).set('layouts', self.layouts);
});
},
gridParas: function() {
var self = this;
var returnObj = {};
returnObj.url = '/myService2';
returnObj.beforeLoadComplete = function(records) {
// Code to use response & set records
return records;
};
return returnObj;
}.property(),
actions: {
}
});
My template looks like
{{my-grid params=this.gridParas elementId='myGrid'}}
My doPost method looks like below;
doPost: function(postData, requestUrl, isAsync){
requestUrl = this.getURL(requestUrl);
isAsync = (isAsync == undefined) ? true : isAsync;
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
return $.ajax({
// settings
}).success(resolve).error(reject);
});
return promise;
}
Given the above setup, I wanted to understand the flow/sequence of execution (i.e. for the different hooks).
I was trying to debug and it kept hopping from one class to another.
Also, 2 specific questions;
I was expecting the "activate" hook to be fired initially, but found out that is not the case. It first executes the "gridParas" hook
i.e. before the "activate" hook. Is it because of "gridParas"
specified in the template ?
When I do this.doPost() for /myService1, it has to be a "sync" request, else the flow of execution changes and I get an error.
Actually I want the code inside filterCols() controller i.e.
this.set('gridVals.layout', this.get('layouts')[this.get('type')]) to
be executed only after the response has been received from
/myService1. However, as of now, I have to use a "sync" request to do
that, otherwise with "async", the execution moves to filterCols() and
since I do not have the response yet, it throws an error.
Just to add, I am using Ember v 2.0
activate() on the route is triggered after the beforeModel, model and afterModel hooks... because those 3 hooks are considered the "validation phase" (which determines if the route will resolve at all). To be clear, this route hook has nothing to do with using gridParas in your template... it has everything to do with callling get('gridParas') within your model hook.
It is not clear to me where doPost() is connected to the rest of your code... however because it is returning a promise object you can tack on a then() which will allow you to essentially wait for the promise response and then use it in the rest of your code.
Simple Example:
this.doPost().then((theResponse) => {
this.doSomethingWith(theResponse);
});
If you can simplify your question to be more clear and concise, i may be able to provide more info
Generally at this level you should explain what you want to archive, and not just ask how it works, because I think you fight a lot against the framework!
But I take this out of your comment.
First, you don't need your doPost method! jQuerys $.ajax returns a thenable, that can be resolved to a Promise with Ember.RSVP.resolve!
Next: If you want to fetch data before actually rendering anything you should do this in the model hook!
I'm not sure if you want to fetch /service1, and then with the response you build a request to /service2, or if you can fetch both services independently and then show your data (your grid?) with the data of both services. So here are both ways:
If you can fetch both services independently do this in your routes model hook:
return Ember.RSVP.hash({
service1: Ember.RSVP.resolve($.ajax(/*your request to /service1 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
service2: Ember.RSVP.resolve($.ajax(/*your request to /service2 with all data and params, may use query-params!*/).then(data => {
return data; // extract the data you need, may transform the response, etc.
},
});
If you need the response of /service1 to fetch /service2 just do this in your model hook:
return Ember.RSVP.resolve($.ajax(/*/service1*/)).then(service1 => {
return Ember.RSVP.resolve($.ajax(/*/service2*/)).then(service2 => {
return {
service1,
service2
}; // this object will then be available as `model` on your controller
});
});
If this does not help you (and I really think this should fix your problems) please describe your Problem.

Meteor Roles package - userIsInRole always returns false

I want to have a filter on routing level, checking if the user is in a specific role.
this.route('gamePage', {
path: '/game/:slug/',
onBeforeAction: teamFilter,
waitOn: function() { return […]; },
data: function() { return Games.findOne({slug: this.params.slug}); }
});
Here is my filter:
var teamFilter = function(pause) {
if (Meteor.user()) {
Meteor.call('checkPermission', this.params.slug, Meteor.userId(), function(error, result) {
if (error) {
throwError(error.reason, error.details);
return null;
}
console.log(result); // returns always false
if (!result) {
this.render('noAccess');
pause();
}
});
}
}
In my collection:
checkPermission: function(gameSlug, userId) {
if (serverVar) { // only executed on the server
var game = Games.findOne({slug: gameSlug});
if (game) {
if (!Roles.userIsInRole(userId, game._id, ['administrator', 'team'])) {
return false;
} else {
return true;
}
}
}
}
My first problem is that Roles.userIsInRole(userId, game._id, ['administrator', 'team'] always returns false. At first, I had this code in my router.js, but then I thought that it does not work because of a missing publication/subscription, so I ensured that the code runs only on the server. I checked the database and the user is in the role.
My second problem is that I get an exception (Exception in delivering result of invoking 'checkPermission': http://localhost:3000/lib/router.js?77b3b67967715e480a1ce463f3447ec61898e7d5:14:28) at this point: this.render('noAccess'); and I don't know why.
I already read this: meteor Roles.userIsInRole() always returning false but it didn't solve my problem.
Any help would be greatly appreciated.
In teamFilter hook you call Meteor.method checkPermission which works asynchronously and OnBeforeAction expects synchronous execution ( no callbacks ). That is why you always receive false.
Another thing is that you are using Roles.userIsInRole incorrectly:
Should be:
Roles.userIsInRole(this.userId, ['view-secrets','admin'], group)
In this case I would check roles on client side:
Roles.userIsInRole(userId, ['administrator', 'team'])
Probably you are worried about security with this solution.
I don't think you should.
What is the most important is data and data is protected by publish function which should check the roles.
Please note that all templates are accessible to client.
You can add roles to the user only on server for that you can user Meteor.call({}); check here method from client to call method on server's main.js and you can check after this method call if the role is added in users collection using meteor mongo and db.users.find({}).pretty() and see if the roles array is added the user of that usedId then you can use Roles.userIsInRole() function anywhere on client to check loggedin users role.

Categories

Resources