react-native dynamically get/set configuration variables (API URL) - javascript

My app starts by choosing a country and I want to change my API_URL according to the country data a user selects.
My country_id is stored in AsyncStorage. I have tried this but is has not worked.
function Configure() {
let url = '';
}
Configure.prototype.getApiUrl = function (params = null) {
AsyncStorage.getItem("country").then((value) => {
if(value == 223) {
return "https://www.website.com/usa_api"
}else{
return "https://www.website.com/api"
}
});
}
module.exports = Configure

You have to return the Promise function
function Configure() {
let url = '';
}
Configure.prototype.getApiUrl = function (params = null) {
return AsyncStorage // Have to return the promise
.getItem("country")
.then((value) => {
if (value == 223) {
return "https://www.website.com/usa_api"
} else {
return "https://www.website.com/api"
}
});
}
module.exports = Configure
Usage
Now that we are returning the Promise, we can await it from where you want to use it
// Use the promise
Configure
.getApiUrl()
.then((apiURL) => {
// You should be getting the API URL here
})
// Or better looking code with async/await
const apiURL = await Configure.getApiUrl();
// You should be getting the API URL here

Related

Update object after function return

I am trying to create a simple Api consume service with JavaScript. This service has a 3 properties (loading, data, error) and I want to change thats properties values dynamicly in promise and I want to use this service like that ->
const { loading, data, err } = new ApiService().send(request).getResults();
My main goal is dynamic loading and data so during Api call loading property's value equals to true and when Api call finished loading property's value equals to false and data property filled with Api response. So I want to use this service like that ->
const { loading, data, err } = new ApiService().setSync(false).send(q).getResults();
if (loading) {
pElement.text("Loading");
} else {
pElement.text(data.id);
}
My Api service codes:
function ApiService(header = {}) {
this._loading = false;
this._data = {};
this._header = header;
this._error = "";
this._isAsync = false;
}
ApiService.prototype.setSync = function (isAsync = false) {
this._isAsync = isAsync;
return this;
};
ApiService.prototype.send = function (request) {
const self = this;
if (!this._isAsync) {
this._loading = true;
request
.then(function (data) {
self._loading = false;
self._data = data;
})
.catch(function (e) {
self._error = e;
});
return this;
} else {
return request
.then(function (data) {
self._loading = false;
self._data = data;
return data;
})
.catch(function (e) {
self._error = e;
});
}
};
ApiService.prototype.getResults = function () {
const self = this;
return { loading: self._loading, data: self._data, err: self._error };
};
module.exports = ApiService;
This service works once and its returns default values as normaly but I want to dynamic update values. Is it possible?
Short answer: No. After a return statement you cant update the values like you have in mind.
Frameworks like React.js do have syntax like this in their Hooks API, but they also have components which are executed again every time a state changes, resulting in having the variables re-evaluated.
In Vanilla JS land the easiest thing to do is to return a Promise, wait for it to finish and then continue. I think that using async / await is the closest thing to making your code behaving as you would like.
The snippet below demonstrates how that would look and run. The returned loading value would always be false as you would only get the result after it's done loading, so it can be omitted.
function ApiService(header = {}) {
this._loading = false;
this._data = {};
this._header = header;
this._error = "";
this._isAsync = false;
}
ApiService.prototype.send = async function(request) {
this._loading = true;
this._data = {};
this._error = "";
try {
const data = await request();
this._data = data;
} catch(error) {
this._error = error;
} finally {
this._loading = false;
return {
data: this._data,
error: this._error,
};
}
}
(async () => {
pElement.text("Loading");
const { data, err } = await new ApiService().send(request);
if (!err) {
pElement.text(data.id);
}
})();

Error while returning value from async dns.lookup() function

I'm writing a function to verify a url using dns.lookup() function as defined below:
const dns = require('dns');
const verifyURL = (url) => {
const protocolRegEx = /^https?:\/\/(.*)/i;
const hostnameRegEx = /^([a-z0-9\-_]+\.)+[a-z0-9\-_]+/i;
if (url.match(/\/$/i)) {
url = url.slice(0,-1);
}
const protocol = url.match(protocolRegEx);
if (!protocol) {
return false;
}
const hostname = protocol[1].match(hostnameRegEx);
if (hostname) {
dns.lookup(hostname[0], (err, adderss) => {
if (err) return false;
return adderss;
})
}
}
I'm trying to return either true or false from inside of the callback passed to the dns.lookup() function.
(async () => {
let x = await verifyURL('https://stackoverflow.com/')
console.log(x);
})();
But everytime I run this code I get undefined as return value.
I already tried this answer, but it's not working.
Please help me. Thank you!!
You are awaiting a function, verifyURL, which does not return a promise.
One option is to modify your verifyURL function to return a promise.
const verifyURL = (url) => {
const protocolRegEx = /^https?:\/\/(.*)/i;
const hostnameRegEx = /^([a-z0-9\-_]+\.)+[a-z0-9\-_]+/i;
if (url.match(/\/$/i)) {
url = url.slice(0,-1);
}
const protocol = url.match(protocolRegEx);
if (!protocol) {
return false;
}
const hostname = protocol[1].match(hostnameRegEx);
//verify this! I add this to return false if !hostname
if (!hostname) {
return false
}
return new Promise((resolve, reject) => {
dns.lookup(hostname[0], (err, address) => {
if (err) {
return reject(err);
}
// Here I resolve to address. You can resolve to true as you mention in your post.
return resolve(address)
});
});
}
Also you should handle the rejection of the promise.
E.g. looking for https://stackoverflow2.com will give you an error "getaddrinfo ENOTFOUND stackoverflow2.com"
(async () => {
try {
let x = await verifyURL('https://stackoverflow.com')
console.log(x);
} catch (e) {
console.error(e.message)
}
})();
Another option is to use dns promise api

Perform two functions simultaneously

I have a function that refreshes the data of my component when the function is called. At this moment it only works for one component at a time. But I want to refresh two components at once. This is my refresh function:
fetchDataByName = name => {
const { retrievedData } = this.state;
const { fetcher } = this.props;
const fetch = _.find(fetcher, { name });
if (typeof fetch === "undefined") {
throw new Error(`Fetch with ${name} cannot be found in fetcher`);
}
this.fetchData(fetch, (error, data) => {
retrievedData[name] = data;
this._isMounted && this.setState({ retrievedData });
});
};
My function is called like this:
refresh("meetingTypes");
As it it passed as props to my component:
return (
<Component
{...retrievedData}
{...componentProps}
refresh={this.fetchDataByName}
/>
);
I tried passing multiple component names as an array like this:
const args = ['meetingTypes', 'exampleMeetingTypes'];
refresh(args);
And then check in my fetchDataByName function if name is an array and loop through the array to fetch the data. But then the function is still executed after each other instead of at the same time. So my question is:
What would be the best way to implement this that it seems like the
function is executed at once instead of first refreshing meetingTypes
and then exampleMeetingTypes?
Should I use async/await or are there better options?
The fetchData function:
fetchData = (fetch, callback) => {
const { componentProps } = this.props;
let { route, params = [] } = fetch;
let fetchData = true;
// if fetcher url contains params and the param can be found
// in the component props, they should be replaced.
_.each(params, param => {
if (componentProps[param]) {
route = route.replace(`:${param}`, componentProps[param]);
} else {
fetchData = false; // don't fetch data for this entry as the params are not given
}
});
if (fetchData) {
axios
.get(route)
.then(({ data }) => {
if (this.isMounted) {
callback(null, data);
}
})
.catch(error => {
if (error.response.status == 403) {
this._isMounted && this.setState({ errorCode: 403 });
setMessage({
text: "Unauthorized",
type: "error"
});
}
if (error.response.status == 401) {
this._isMounted && this.setState({ errorCode: 401 });
window.location.href = "/login";
}
if (error.response.status != 403) {
console.error("Your backend is failing.", error);
}
callback(error, null);
});
} else {
callback(null, null);
}
};
I assume fetchData works asynchronously (ajax or similar). To refresh two aspects of the data in parallel, simply make two calls instead of one:
refresh("meetingTypes");
refresh("exampleMeetingTypes");
The two ajax calls or whatever will run in parallel, each updating the component when it finishes. But: See the "Side Note" below, there's a problem with fetchDataByName.
If you want to avoid updating the component twice, you'll have to update fetchDataByName to either accept multiple names or to return a promise of the result (or similar) rather than updating the component directly, so the caller can do multiple calls and wait for both results before doing the update.
Side note: This aspect of fetchDataByName looks suspect:
fetchDataByName = name => {
const { retrievedData } = this.state; // <=============================
const { fetcher } = this.props;
const fetch = _.find(fetcher, { name });
if (typeof fetch === "undefined") {
throw new Error(`Fetch with ${name} cannot be found in fetcher`);
}
this.fetchData(fetch, (error, data) => {
retrievedData[name] = data; // <=============================
this._isMounted && this.setState({ retrievedData });
});
};
Two problems with that:
It updates an object stored in your state directly, which is something you must never do with React.
It replaces the entire retrievedData object with one that may well be stale.
Instead:
fetchDataByName = name => {
// *** No `retrievedData` here
const { fetcher } = this.props;
const fetch = _.find(fetcher, { name });
if (typeof fetch === "undefined") {
throw new Error(`Fetch with ${name} cannot be found in fetcher`);
}
this.fetchData(fetch, (error, data) => {
if (this._isMounted) { // ***
this.setState(({retrievedData}) => ( // ***
{ retrievedData: {...retrievedData, [name]: data} } // ***
); // ***
} // ***
});
};
That removes the in-place mutation of the object with spread, and uses an up-to-date version of retrievedData by using the callback version of setState.

JavaScript: Returning array from recursive function

I have made a class which builds some data from api:
const http = require("http");
class VideoService {
constructor() {
this.items = [];
}
fetchVideos(token = "") {
const url = `https://www.example.com`;
http.getJSON(url).then((results) => {
results.items.forEach((item, index) => {
const vid = item.snippet.resourceId.videoId;
this.items.push({
title: item.title,
date: item.publishedAt
});
console.log(this.items.length); // here length inreases, works here
});
if (typeof results.nextPageToken !== "undefined") {
return this.fetchVideos(results.nextPageToken);
}
});
}
getVideos() {
this.fetchVideos();
console.log(this.items.length); // this returns 0 instead of all items fetched
return this.items;
}
}
module.exports = VideoService;
In another file, I am using it like this:
const videoService = require("../shared/videoService");
const videos = (new videoService()).getVideos();
console.log(videos);
The last console.log call always returns empty array instead of all data collected in items property of the above class.
Can anybody tell what I am missing here?
This happens because in your function fetchVideos(), you are making an http call which will be processed asynchronously. You can try to process it this way.
fetchVideos(token = "") {
const url = `https://www.example.com`;
return http.getJSON(url).then((results) => {
results.items.forEach((item, index) => {
const vid = item.snippet.resourceId.videoId;
this.items.push({
title: item.title,
date: item.publishedAt
});
console.log(this.items.length); // here length inreases, works here
});
if (typeof results.nextPageToken !== "undefined") {
return this.fetchVideos(results.nextPageToken);
}
else return new Promise((resolve, reject)=>{
resolve();
});
});
}
getVideos() {
return this.fetchVideos().then(function(){
console.log(this.items.length); // this returns 0 instead of all items fetched
return this.items;
});
}
I suggest reading about promises and asynchronicity in javascript. Check this link:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Can we use await and same functions for promises?

So i have request coming based on that i am building this.request array so i can make calls to urls and request.body that is pushed into array , In below code trying to use RequestResponseHandler.processRequest for both promises but its always going into spec(duplicate call) when i call same function processRequest for other promise , How can i make sure if processRequest is called for spec ignore that if condition and go to PTM
request
{ header: { lineOfBusiness: ["spec","PTM"] } , body: data }
handler.ts
export class RequestResponseHandler {
public static processRequest(data: any, url: string): Promise < any > {
const reqObject: IRequestURL = {}
as IRequestURL;
const lob = data.header.lineOfBusiness;
if (lob[0] === "spec") {
const specUrl = urlConfig + url;
reqObject.url = specUrl;
reqObject.body = data;
}
if (lob[1] === "PTM") {
const ptmUrl = urlConfig + url;
reqObject.url = ptmUrl;
reqObject.body = data;
}
return Promise.resolve(reqObject);
}
}
controllet.ts
const bRetSpec: boolean = await this.specBalanceRequest(request);
const bRetPtm: boolean = await this.ptmBalanceRequest(request);
if (!bRetPtm && !bRetSpec) {
return Promise.reject(new Error("Processing failed"));
}
try {
const __data: IResponse = await makeRequest(this._request);
const resp1 = await another promise to resolve _data[0];
const resp2 = await another promise to resolve _data[1];
return await Promise.all([resp1, resp2]);
} catch (err) {
return Promise.reject(err);
}
private async specBalanceRequest(#Body() request: ExpressRequest): Promise < boolean > {
const specUrl = "/payments";
const reqObject = await RequestResponseHandler.processRequest(request.body, specialtyUrl);
this._request.push(reqObject);
return Promise.resolve(true);
}
private async ptmBalanceRequest(#Body() request: any): Promise < boolean > {
const careURL = "/Order";
const reqObject = await RequestResponseHandler.processRequest(request.body, careURL);
this._request.push(reqObject);
return Promise.resolve(true);
}
One thing that I suggest is to not make unnecessary promise function. Keep it simple as possible.
RequestResponseHandler.processRequest doesn't need to be a promise
export class RequestResponseHandler {
public static processRequest(data: any, url: string): IRequestURL {
const reqObject: IRequestURL = {} as IRequestURL;
const lob = data.header.lineOfBusiness;
// I guess we can improve code below, no diff between "spec" and "ptm"
if (lob[0] === "spec") {
const specUrl = urlConfig + url;
reqObject.url = specUrl;
reqObject.body = data;
}
if (lob[1] === "PTM") {
const ptmUrl = urlConfig + url;
reqObject.url = ptmUrl;
reqObject.body = data;
}
return reqObject; // return value not a promise
}
}
in controller file
const bRetSpec: boolean = this.specBalanceRequest(request); // remove `await`
const bRetPtm: boolean = this.ptmBalanceRequest(request); // remove `await`
if (!bRetPtm && !bRetSpec) {
return Promise.reject(new Error("Processing failed"));
}
try {
const __data: IResponse = await makeRequest(this._request);
// the two promise calls, I guess can be move to be inside promise.all
return await Promise.all([anotherPromiseData0, anotherPromiseData1]);
} catch (err) {
return Promise.reject(err);
}
// make it non promise
private specBalanceRequest(#Body() request: ExpressRequest): void {
const specUrl = "/payments";
const reqObject = RequestResponseHandler.processRequest(request.body, specialtyUrl);
this._request.push(reqObject);
}
// make it non promise
private ptmBalanceRequest(#Body() request: any): void {
const careURL = "/Order";
const reqObject = RequestResponseHandler.processRequest(request.body, careURL);
this._request.push(reqObject);
}

Categories