How to prevent axios from posting before state is updated - javascript

I am having a problem that the post request is not sending the updated currentVideo state as setState seems to be nonblocking. How can i make axios wait for the state to be set?
const nextVideo = () => {
if (videoList.indexOf(currentVideo) < videoList.length - 1) {
setCurrentVideo(videoList[videoList.indexOf(currentVideo) + 1]);
setLoading(true);
axios
.post(`${BACK_PORT}/videos/download`, currentVideo)
.then(function (response) {
if (response.data) {
setLoading(false);
} else {
console.log("waiting...");
}
})
.catch(function (error) {
alert(error)
});
} else {
alert("This is the last video");
}
};

Use a variable and store the currentvideo value in it. Then use that variable at both the places,i.e., for setting up the state and making the axios call.
const nextVideo = () => {
if (videoList.indexOf(currentVideo) < videoList.length - 1) {
let currentVideo = videoList[videoList.indexOf(currentVideo) + 1] //change here
setCurrentVideo(currentVideo );
setLoading(true);
axios
.post(`${BACK_PORT}/videos/download`, currentVideo)
.then(function (response) {
if (response.data) {
setLoading(false);
} else {
console.log("waiting...");
}
})
.catch(function (error) {
alert(error)
});
} else {
alert("This is the last video");
}
};

Related

React native: Axios Get accusing Network Error

I'm coding my first react-native program with Axios and found problems making get calls. Sinse it's a Network error I assume the problem is somewhere below.
Api.js:
import axios from "axios";
const api = axios.create({
baseURL: "https://192.168.15.8:3333",
});
export default api;
index.js
async function loadIncidents() {
if (loading) {
return;
}
if (total > 0 && incidents.length === total) {
return;
}
setLoading(true);
try {
const response = await api.get("/incidents", {
params: { page },
});
setIncidents([...incidents, ...response.data]);
setTotal(response.headers["x-total-count"]);
setPage(page + 1);
} catch (err) {
console.log('Ocorreu um erro: ', err)
}
setLoading(false);
}
useEffect(() => {
loadIncidents();
}, []);
you can use try() catch()
async function loadIncidents() {
if (loading) {
return;
}
if (total > 0 && incidents.length === total) {
return;
}
setLoading(true);
let response;
try {
response = await api.get("incidents", {
params: { page },
});
} catch (ex) {
console.log(ex);
}
setIncidents([...incidents, ...response.data]);
setTotal(response.headers["x-total-count"]);
setPage(page + 1);
setLoading(false);
}

How can I get the response from dispatch?

I have a component which has a form where at the moment to do clic on submit button, I call a function handleSubmit (it is on my component), this function call an action through of dispatch and this action, I make a call to a service (HTTP Request).
handleSubmit
handleSubmit = (e) => {
e.preventDefault()
const { validateFields } = this.props.form;
validateFields((err, params) => {
if (!err) {
const { user, dispatch } = this.props;
let response = dispatch(actions.addDevice(params))
console.log(response); //Response is undefined
}
});
}
actions.addDevice
function addDevice(params){
return (dispatch, getState) => {
let { authentication } = getState();
dispatch(request({}));
service.addDevice(params, authentication.user.access_token)
.then(
response => {
if(response.status === 201) {
dispatch(success(response.data));
}
return response;
},
error => {
dispatch(failure(error.toString()));
dispatch(alertActions.error(error.toString()));
}
)
}
function request(response) { return { type: constants.ADD_DEVICE_REQUEST, response } }
function success(response) { return { type: constants.ADD_DEVICE_SUCCESS, response } }
function failure(error) { return { type: constants.ADD_DEVICE_FAILURE, error } }
}
service.addDevice
function addDevice(params, token){
return axios({
url: 'http://localhost:5000/user/add-device',
method: 'POST',
headers: { 'Authorization': 'Bearer ' + token},
data: {
data1: params.data1,
data2: params.data2,
data3: params.data3
}
})
.then(function(response) {
return response;
})
.catch(function(error) {
return error.response;
});
}
I would like to get the response in my component to be able to make validations but as the request is async, I never can get the response and only prints an undefined variable. How can I get the response sync? Or what do I need do to be able to make validations?
You are not returning the promise service.addDevice.
So you can do return service.addDevice... and in the handleSubmit you do dispatch(...).then((data) => ...do something with the data...)
let response = dispatch(actions.addDevice(params))
this is asynchronous. So it is not surprising to return undefined from console.log(). console.log() execute even before dispatch process is completed. Use promise or async await syntax. I would recommend using the async-await syntax.
handleSubmit = (e) => {
e.preventDefault()
const { validateFields } = this.props.form;
validateFields(async (err, params) => {
if (!err) {
const { user, dispatch } = this.props;
let response =await dispatch(actions.addDevice(params))
console.log(response); //Response is undefined
}
});
}
Please replace your code with this code
handleSubmit
handleSubmit = (e) => {
e.preventDefault()
const { validateFields } = this.props.form;
validateFields((err, params) => {
if (!err) {
const { user, dispatch } = this.props;
dispatch(actions.addDevice(params)).then((response)=>{
console.log(response);
})
}
});
}
actions.addDevice
function addDevice(params){
return (dispatch, getState) => {
let { authentication } = getState();
dispatch(request({}));
return service.addDevice(params, authentication.user.access_token)
.then(
response => {
if(response.status === 201) {
dispatch(success(response.data));
}
return response;
},
error => {
dispatch(failure(error.toString()));
dispatch(alertActions.error(error.toString()));
}
)
}
function request(response) { return { type: constants.ADD_DEVICE_REQUEST, response } }
function success(response) { return { type: constants.ADD_DEVICE_SUCCESS, response } }
function failure(error) { return { type: constants.ADD_DEVICE_FAILURE, error } }
}

wait for asynchronous functions to finish in Angular

So I was working on a new component in Angular and in the ngOninit I have the following asynchronous functions below...
This.getUserProfile needs to be finished before I can call this.getPrivateGroup() and this.getPrivateGroup() needs to be finished before I can call this.loadGroupPosts(). I know I could write these functions inside the callback of the asynchronous requests, but I was wondering if there is a way to keep it in ngOnInit to keep it cleaner?
Anyone has an idea?
ngOnInit() {
this.getUserProfile();
// my-workplace depends on a private group and we need to fetch that group and edit
// the group data before we proceed and get the group post
if (this.isItMyWorkplace) {
this.getPrivateGroup();
}
this.loadGroupPosts();
}
getUserProfile() {
this._userService.getUser()
.subscribe((res) => {
this.user = res.user;
console.log('log user', this.user);
this.profileImage = res.user['profile_pic'];
this.profileImage = this.BASE_URL + `/uploads/${this.profileImage}`;
}, (err) => {
this.alert.class = 'alert alert-danger';
if (err.status === 401) {
this.alert.message = err.error.message;
setTimeout(() => {
localStorage.clear();
this._router.navigate(['']);
}, 3000);
} else if (err.status) {
this.alert.class = err.error.message;
} else {
this.alert.message = 'Error! either server is down or no internet connection';
}
});
}
getPrivateGroup() {
console.log('user check', this.user);
this.groupService.getPrivateGroup(`${this.user.first_name}${this.user.last_name}`)
.subscribe((group) => {
console.log('received response', group)
})
}
// !--LOAD ALL THE GROUP POSTS ON INIT--! //
loadGroupPosts() {
this.isLoading$.next(true);
this.postService.getGroupPosts(this.group_id)
.subscribe((res) => {
// console.log('Group posts:', res);
this.posts = res['posts'];
console.log('Group posts:', this.posts);
this.isLoading$.next(false);
this.show_new_posts_badge = 0;
}, (err) => {
swal("Error!", "Error while retrieving the posts " + err, "danger");
});
}
// !--LOAD ALL THE GROUP POSTS ON INIT--! //
You can use basic promises with async/await.
async ngOnInit() {
await this.getUserProfile(); // <-- 1. change
// my-workplace depends on a private group and we need to fetch that group and edit
// the group data before we proceed and get the group post
if (this.isItMyWorkplace) {
this.getPrivateGroup();
}
this.loadGroupPosts();
}
async getUserProfile() {
this._userService.getUser()
.subscribe((res) => {
this.user = res.user;
console.log('log user', this.user);
this.profileImage = res.user['profile_pic'];
this.profileImage = this.BASE_URL + `/uploads/${this.profileImage}`;
return true; // <-- this
}, (err) => {
this.alert.class = 'alert alert-danger';
if (err.status === 401) {
this.alert.message = err.error.message;
setTimeout(() => {
localStorage.clear();
this._router.navigate(['']);
}, 3000);
} else if (err.status) {
this.alert.class = err.error.message;
} else {
this.alert.message = 'Error! either server is down or no internet connection';
}
throw err;
});
}
You could instead leverage RxJS and use a switchMap something like this (syntax NOT checked):
getData(): Observable<string[]> {
return this._userService.getUser()
.pipe(
switchMap(userInfo=> {
return this.getPrivateGroup();
}),
catchError(this.someErrorHandler)
);
}
One way to do is, return the Observable instead of subscribing in the getPrivateGroup()
getPrivateGroup() {
console.log('user check', this.user);
return this.groupService.getPrivateGroup(`${this.user.first_name}${this.user.last_name}`)
}
And then, subscribe to the data where you want the chain the this.loadGroupPosts()
if (this.isItMyWorkplace) {
this.getPrivateGroup().subscribe(group => {
this.group = group; //you probably want to assign the group data
this.loadGroupPosts()});
}
you could also use the 3rd part of your subscribe function when its completed
i am not quite sure if this is a clean solution, in my opinion it is.
ngOnInit() {
this.getUserProfile();
}
getUserProfile() {
this._userService.getUser()
.subscribe((res) => {
this.user = res.user;
console.log('log user', this.user);
this.profileImage = res.user['profile_pic'];
this.profileImage = this.BASE_URL + `/uploads/${this.profileImage}`;
}, (err) => {
this.alert.class = 'alert alert-danger';
if (err.status === 401) {
this.alert.message = err.error.message;
setTimeout(() => {
localStorage.clear();
this._router.navigate(['']);
}, 3000);
} else if (err.status) {
this.alert.class = err.error.message;
} else {
this.alert.message = 'Error! either server is down or no internet connection';
}
}, () => {
// my-workplace depends on a private group and we need to fetch that group and edit
// the group data before we proceed and get the group post
if (this.isItMyWorkplace) {
this.getPrivateGroup();
}
});
}
getPrivateGroup() {
console.log('user check', this.user);
this.groupService.getPrivateGroup(`${this.user.first_name}${this.user.last_name}`)
.subscribe((group) => {
console.log('received response', group)
}, error => {
console.log(error)
}, () => {
this.loadGroupPosts();
})
}
loadGroupPosts() {
this.isLoading$.next(true);
this.postService.getGroupPosts(this.group_id)
.subscribe((res) => {
// console.log('Group posts:', res);
this.posts = res['posts'];
console.log('Group posts:', this.posts);
this.isLoading$.next(false);
this.show_new_posts_badge = 0;
}, (err) => {
swal("Error!", "Error while retrieving the posts " + err, "danger");
});
}

Object fetch same api endpoint

I'm creating two instances of this object on every page of my website. When it loads, it
the makes a fetch call to /cities.json for data, that it then stores in session storage.
Next time the object
is created (on another page), it checks session storage before calling the api endpoint, to prevent
needless data requests. However, when the object is first created it makes two calls to the /cities.json api - because two instances typehead exist.
Apart from moving the call to outside of the object creation, which I don't want to do, as other objects also require this same api
endpoint, and I'd like to find a pattern that could work for each.
Index.js
import Typeahead from '../typeahead'
var typeaheadElements = [].slice.call(document.getElementsByClassName('typeahead'));
if (typeaheadElements.length) {
typeaheadElements.map(function (element) {
new Typeahead(element);
});
}
Typeahead.js
import fetchPromise from '../utilities/fetchSetStorage.js';
function Typeahead(element) {
init.call(this);
function init() {
var input = element.getElementsByTagName('input')[0];
if (input) {
let citiesData = fetchPromise('./cities.json', 'citiesJson');
Promise.all([citiesData]).then(function(values) {
items = values[0];
element.getElementsByTagName('input')[0].removeAttribute('disabled');
});
input.addEventListener('input', handleChange.bind(this));
element.addEventListener('keydown', handleKeyDown.bind(this));
element.addEventListener('click', handleElementClick.bind(this));
document.addEventListener('click', handleDocumentClick.bind(this));
}
}
}
fetchSetStorage.js
export default function fetchPromise(url, storageKey) {
if(sessionStorage.getItem(storageKey)) {
let storageKeyVal = JSON.parse(sessionStorage.getItem(storageKey));
return storageKeyVal;
}
return fetch(url)
.then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
return response;
})
.then(response => response.json())
.then(response => {
sessionStorage.setItem(storageKey, JSON.stringify(response))
return response;
})
.catch(function(err) {
console.log('Fetch Error :-S', err);
})
}
function Typeahead(element) {
init.call(this);
function init() {
var input = true;
if (input) {
console.log('input')
let citiesData = fetchPromise('/echo/json/', 'citiesJson');
Promise.all([citiesData]).then(function(values) {
items = values[0];
console.log(values);
});
}
}
}
function fetchPromise(url, storageKey) {
if (sessionStorage.getItem(storageKey)) {
let storageKeyVal = JSON.parse(sessionStorage.getItem(storageKey));
return storageKeyVal;
}
return fetch(url)
.then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
return response;
})
.then(response => response.json())
.then(response => {
sessionStorage.setItem(storageKey, JSON.stringify(response))
return response;
})
.catch(function(err) {
console.log('Fetch Error :-S', err);
})
}
var typeaheadElements = [].slice.call(document.getElementsByClassName('typeahead'));
if (typeaheadElements.length) {
console.log('typeaheadfound');
typeaheadElements.map(function(element) {
new Typeahead(element);
});
}
<div class="typeahead">
Typeahead
</div>
<div class="typeahead">
Typeahead
</div>
My "dirty" solution:
Typeahead.js
import fetchPromise from '../utilities/fetchSetStorage.js';
let isFetched = false;
function Typeahead(element) {
init.call(this);
function init() {
var input = element.getElementsByTagName('input')[0];
if (input && !isFetched) {
isFetched = true;
let citiesData = fetchPromise('./cities.json', 'citiesJson');
Promise.all([citiesData]).then(function(values) {
items = values[0];
element.getElementsByTagName('input')[0].removeAttribute('disabled');
});
input.addEventListener('input', handleChange.bind(this));
element.addEventListener('keydown', handleKeyDown.bind(this));
element.addEventListener('click', handleElementClick.bind(this));
document.addEventListener('click', handleDocumentClick.bind(this));
}
}
}

How to deal with empty response in axios?

I use axios with vue.js to fetch data as unlimited pagination. It works fine expect when there is no data to render:
fetchData() {
this.loading = true
this.page++;
axios.get(this.BASE_URL + '/api/jokes/'+'?page='+this.page).then(
response =>
//how to exist if there is no data in the response?
this.jokes = response.data).catch(function (error) {
console.log(error);
});
I'm wondering how to stop rendering when we reached to the last page and there is no more data to display?
I've looked at the docs but could not find my answer.
Maybe introducing some basic flag logics. I have taken the freedom to assume, but you can always define your logic
fetchData() {
this.loading = true;
if (this.page > 0) {
axios.get(this.BASE_URL + '/api/jokes/page=' + this.page)
.then(response => {
const isDataAvailable = response.data && response.data.length;
this.jokes = isDataAvailable ? response.data : [];
this.page = isDataAvailable ? (this.page + 1) : 0;
})
.catch(function(error) {
console.error(error);
});
}
}
You could throw a custom error that will be catch by the catch method.
fetchData() {
this.loading = true
this.page++;
axios.get(this.BASE_URL + '/api/jokes/'+'?page='+this.page).then(
response => {
//how to exist if there is no data in the response?
if (!response.data || response.data.length == 0) {
const emptyDataError = new Error('Invalid data');
emptyDataError.statusCode = 500;
throw emptyDataError;
}
this.jokes = response.data;
}).catch(function (error) {
console.log(error);
});

Categories