How to make synchronous API call request in react js - reactjs

i am beginner to react js i am working on a small application which makes api requests frequently. So the problem i am facing is there is a page with form fields prefilled from the db, if the user makes changes to those fields i am posting the new filed values to db. when submit button is clicked saveAndConttinue() is called,from there addNewAddress() is invoked based on the condition. But the problem is the response that i get from addNewAddress has to be used for the next api call in the queue, but it is taking time to get response, and the address_id is having null values for it's post call. is there any way to make synchronous call in react with out using flux/redux for now?
saveAndContinue(e) {
e.preventDefault();
if(this.props.params.delivery === 'home_delivery' && this.state.counter) {
this.addNewAddress();
}
console.log('add id is '+this.state.address_id);
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
fd.append('token', this.props.params.token);
fd.append('dish_id', this.props.params.dish_id);
fd.append('address_type', this.props.params.delivery);
fd.append('address_id', this.state.address_id);
fd.append('ordered_units', this.props.params.quantity);
fd.append('total_cost', this.props.params.total_cost);
fd.append('total_service_charge', this.props.params.service_charge);
fd.append('net_amount', this.props.params.net_cost);
fd.append('hub_id', this.props.params.hub_id);
fd.append('delivery_charge', this.props.params.delivery_charge);
fd.append('payment_type', this.state.payment_type);
fd.append('device_type', 'web');
axios.post(myConfig.apiUrl + '/api/foody/orders/purchase' , fd, config)
.then(function(response){
if(response.data.success) {
console.log(response);
browserHistory.push('/order_confirmed/');
} else {
console.log(response);
//alert(response.data.message)
}
});
}
addNewAddress() {
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
if(this.props.params.user_type === 'new') {
fd.append('type', 'PRIMARY');
}
fd.append('token', this.props.params.token);
fd.append('block', this.state.block);
fd.append('door_num', this.state.door_num);
fd.append('address', this.props.params.address);
fd.append('locality', this.props.params.locality);
fd.append('landmark', this.props.params.landmark);
fd.append('hub_id', this.props.params.hub_id);
axios.post(myConfig.apiUrl + '/api/user/add-address' , fd, config)
.then(function(response){
this.setState({address_id: response.data.data['id']});
console.log(this.state.address_id);
}.bind(this));
}

You're going to have to call the next request in the queue after addNewAddress() in the returned promise of addNewAddress():
addNewAddress() {
axios.post()...
.then(function (response) {
// Set state
this.setState({address_id: response.data.data['id']});
// [Call next API request here]
// ...
})
}
Doing synchronous calls are always a bad idea, I personally would advise against it and just do the next call in the returned promise as shown above.

Related

sveltekit TypeError: immutable

I'm using sveltekit 1.0.0-next.483 running with npm run dev -- --host
connecting to an endpoint with a mobile device i get this error:
typeError: immutable
at Headers.append ([..]node_modules/undici/lib/fetch/headers.js:227:13)
This error only occurs on mobile device, connecting to the local net ip address.
my endpoint: src/routes/gqlendpoint/+server.ts
const base = 'http://localhost:4000/graphql';
export async function POST( opts: { request: Request} ): Promise<Response> {
const { request } = opts;
const body = await request.json();
const response = await fetch(base, {
//credentials:"include",
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
return response;
}
the only way I found to unlock this situation, is by commenting a line of code inside node_modules/undici/lib/fetch/headers.js
// 3. If headers’s guard is "immutable", then throw a TypeError.
// 4. Otherwise, if headers’s guard is "request" and name is a
// forbidden header name, return.
// Note: undici does not implement forbidden header names
if (this[kGuard] === 'immutable') {
**//throw new TypeError('immutable')**
} else if (this[kGuard] === 'request-no-cors') {
// 5. Otherwise, if headers’s guard is "request-no-cors":
// TODO
}
which is certainly not a good solution.
You have to return new Response body to avoid this issue , see example code below.
return new Response('test example')
in place of return response;

My fetch doesn't upload the JSON string, I can't see the error in my code

I'm using Slim v4 for my REST API for testing purposes.
I want to fetch a JSON Data string to my REST API for saving some events.
public async callSaveEvent(event: EventList) {
let url: string = config.basePath + "eventList/saveEventList";
console.table(JSON.stringify(event));
await fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event })
}).then(response => {
if (!response.ok) {
throw new Error("Something is bad");
}
}).catch(error => {
console.error("Das ist passiert!: ", error);
});
}
This is my current Code. If I use the fetch.options.mode == "cors", I recieve in Slim that this Method is not allowed. Method is OPTIONS instead of POST. Because of this I using mode == "no-cors".
$param = $req->getParsedBody();
$param_ = $param;
$resp->getBody()->write($param);
return $resp;
}
This is my Backend Code. When I try to read the parsedBody, its just empty.
If I send a request with PostMan its accept the data and I get the data in the $param variable.
Can someone find some errors? I can't find them.

How to check whether response JSON of an API is empty or has an error?

I am new to reactjs and I am stuck in one problem. I am calling an Update API which is of PUT type. I use the fetch function to call the API in reactjs and I check the response of the API. If Response is 200 OK, then I return the response.json() and then check whether the json object has error in it or not. If it has error, then I print the error else I update it.
But when there is no Error present in the response, then I get a syntax-error in return response.json() statement and If there is actually a Error present in the response then there is no syntax-error shown. So is there a method to check whether the response is empty or not so that accordingly I can return response.json().
I have tried by putting a condition as if(response.json() != '') but it shows error in response.json() statement.
fetch( API + name , {
method: 'PUT',
headers: {
'Accept' : 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('access_token')
},
body: JSON.stringify({
name: name,
description: updateDesc
}),
}).then(function(response) {
if(response.status == '200'){
flag=true;
return response.json();
}
else {
flag=false
}
})
.then(json => {
if(flag)
{
if(json.Error != "")
{
that.createNotification('error','update');
}
else {
this.createNotification('success','update');
}
}
});
Unhandled Rejection (SyntaxError): Unexpected end of JSON input
There are multiple issues with this imo:
The callback should be refactored to avoid the use of the flag variable. The code in the function supplied to handlers like then, catch and finally of promises is executed asynchronously. Therefore you cannot be sure / (should not assume) when this value will be assigned and in which state your context is at that time.
.then(json => { if there is an error this will actually use the promise returned by fetch aka response and not the promise returned by response.json() (Currently return response.json() is only executed in the success case)
Note that this happens (currently works in the error case) because you can chain promises. You can find more info and examples about this here
I would refactor the handling of the fetch promise like this:
You can shorten the following example and avoid assigning the promises, but it makes the example better readable
More information about the response object
const fetchPromise = fetch(<your params>);
fetchPromise.then(response => {
if (response.ok()){
//Your request was successful
const jsonPromise = response.json();
jsonPromise.then(data => {
console.log("Successful request, parsed json body", data);
}).catch(error => {
//error handling for json parsing errors (empty body etc.)
console.log("Successful request, Could not parse body as json", error);
})
} else {
//Your request was not successful
/*
You can check the body of the response here anyways. Maybe your api does return a json error?
*/
}
}).catch(error => {
//error handling for fetch errors
}))

Multiple file uploads to Cloudinary with Axios in React

I have tried implementing the superagent way of uploading multiple files in axios. But somehow, I'm getting an error in console
Failed to load https://api.cloudinary.com/v1_1/xxxx/image/upload:
Request header field Authorization is not allowed by
Access-Control-Allow-Headers in preflight response.
My upload handler looks like this
uploadFile(){
const uploaders = this.state.filesToBeSent.map(file => {
const formData = new FormData();
formData.append("file", file);
formData.append("upload_preset", "xxxxx");
formData.append("api_key", "xxxxx");
formData.append("timestamp", (Date.now() / 1000) | 0);
return axios.post(url, formData, {
headers: { "X-Requested-With": "XMLHttpRequest" },
}).then(response => {
const data = response.data;
const fileURL = data.secure_url
console.log(data);
})
});
// Once all the files are uploaded
axios.all(uploaders).then(() => {
// ... perform after upload is successful operation
console.log("upload completed ", uploaders);
});
}
I have got this example from here
Another thing is confusing to me. In superagent we can attach parameters to the request field which includes API Secret Key of Cloudinary like this:
const paramsStr = 'timestamp='+timestamp+'&upload_preset='+uploadPreset+secretKey;
const signature = sha1(paramsStr);
const params = {
'api_key': 'xxxx',
'timestamp': timestamp,
'upload_preset': uploadPreset,
'signature': signature
}
Object.keys(params).forEach(key => {
uploadRequest.field(key, params[key])
});
But in that example, it is not mentioned how to append the secret key and other params to axios.
You will need to generate the signature on your backend, and then perform the upload with the generated signature.
You can generate a signature via the following instructions- https://support.cloudinary.com/hc/en-us/articles/203817991-How-to-generate-a-Cloudinary-signature-on-my-own-
You can also take a look at the following example on how to append the signature to your request. It's in PHP, however, the guidelines still apply.
https://gist.github.com/taragano/a000965b1514befbaa03a24e32efdfe5

Authentication in Angular 2, handling the observables

I just started with a Angular 2 project and am trying to get authentication up and running. Inspired by this tutorial I decided to do the following:
Create a custom RouterOutlet class (extending it) to handle the authentication logic whenever a url is called.
I succeeded in this custom class, but am still not sure how to check if a user is authenticated. My situation is as follows, I need to query a get call to a external API, for my development proces it is as follows:
getAdmin() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get('http://localhost:3000/admin/is_admin.json', options)
.map(res => res)
.catch(this.handleError)
}
This API call returns true or false. I was wondering what would be the best option to use this information? Should I for example call the following function each time a URL should be checked?:
isAdmin() {
this.getAdmin().subscribe(
data => this.authenticationResult = data,
error => console.log("Error: ", error),
() => return JSON.parse(this.authenticationResult._data);
}
I can't get this up and running because my observable is undefined when using the function I gave as example.
The "problem" is that your method is asynchronous so you need to be careful the way and when you use it.
If you want to use within the activate method of your custom RouterOutlet, you need to leverage observables and reactive programming.
I don't know exactly the way you want to check admin roles:
activate(instruction: ComponentInstruction) {
return this.userService.getAdmin().flatMap((isAdmin) => {
if (this.userService.isLoggIn()) {
if (this._canActivate(instruction.urlPath, isAdmin) {
return Observable.fromPromise(super.activate(instruction));
} else {
this.router.navigate(['Forbidden']);
return Observable.throw('Forbidden');
}
} else {
this.router.navigate(['Login']);
return Observable.throw('Not authenticated');
}
}).toPromise();
}
_canActivate(url, admin) {
return this.publicRoutes.indexOf(url) !== -1
|| this.userService.isLoggedIn();
}
In order to optimize the request, you could lazily (and only once) call the request to check if the user is admin or not:
isAdmin:boolean;
getAdmin() {
if (this.isAdmin) {
return Observable.of(this.isAdmin);
} else {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get('http://localhost:3000/admin/is_admin.json', options)
.map(res => res)
.catch(this.handleError);
}
}
Another approach will be also to load this hint when authenticating the user. This way, the implementation of the activate method would be simplier:
activate(instruction: ComponentInstruction) {
if (this.userService.isLoggIn()) {
if (this.userService.isAdmin()) {
return super.activate(instruction);
} else if (this._canActivate(instruction.urlPath, isAdmin) {
return super.activate(instruction);
} else {
this.router.navigate(['Forbidden']);
}
} else {
this.router.navigate(['Login']);
}
}
_canActivate(url, admin) {
return this.publicRoutes.indexOf(url) !== -1
|| this.userService.isLoggedIn();
}
I would consider to call getAdmin() somehow as first Step of your app, store the result in a SessionService object which you move around using Dependency Injection. This way any time you need to check the result of getAdmin you can ask the SessionService instance.
I hope this helps

Resources