Cannot call callback functions on setState in componentDidMount ?? - React - javascript

Consider the following simple code:
componentDidMount() {
this._fetchData();
}
_fetchData() {
let url = UrlFormatter() + '/api/v1/blogs/';
$.get(url, (result) => {
if (result.status === 401) {
this.setState({
error: 'Your session has expired. We cannot load data.',
});
} else {
console.log('obvs here');
this.setState({
error: null,
data: result,
}, () => {
console.log('dasddsa');
this._setUpPostCollapseStatus();
});
}
}).fail((response) => {
this.setState({
error: 'Could not fetch blogs, something went wrong.'
});
});
}
If we investigate the console we see:
obvs here
But we never see: dasddsa, now either this is a bug, or you cant call a callback function on setState in componentDidMount - Or I fail at ES6.
Ideas?

Hm, I wasn't able to replicate this; not sure if this'll be helpful, but here's an example of resolving a promise in componentDidMount and using the setState callback:
http://codepen.io/mikechabot/pen/dXWQAr?editors=0011
promise
const promise = new Promise(resolve => {
setTimeout(() => {
resolve('Fetched data!')
}, 2000)
})
component
componentDidMount() {
console.log('Mounting...');
promise
.then((data) => {
this.setState({ data }, () => {
console.log('Data loaded')
})
})
.catch(error => {
console.log('Error', error);
})
}
console
> "Mounting..."
> "Data loaded"

Related

How to handle errors from within a callback in Nodejs

I have this code that invokes a function and has a callback with error and data parameters:
app.get('/lights', (req,res) => {
hue.getLights(function(err, data){
if(err) res.status(401).send("An error occured: ", err.message);
res.send(data);
});
})
The function that it invokes is:
let getLights = function(callback){
fetch(`http://${gateway}/api/${username}/lights`, {
method: 'GET'
}).then((res) => {
if(res.ok){
return res.json();
}else{
throw new Error(res.message);
}
}).then((json) => {
lightsArray = []
for (var i in json){
lightsArray.push(`ID: ${i} Name: ${json[i]['name']}`);
}
return callback(lightsArray);
});
}
When I make an error occur, the error isn't caught, nor is any error displayed, the app crashes with the message: UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().
Now I know I'm missing a lot, this is my first time using callbacks, let alone handling errors.
Could someone help me with making the error callback work, also show me some flaws in what I'm doing, as I know this won't catch every error that may happen, only errors caused by using the fetch function.
Thanks!
This is my other function (similar but uses a catch aswell, which I think I have done incorrectly too):
let getLightDetails = function (ID, callback) {
fetch(`http://${gateway}/api/${username}/lights/${ID}`, {
method: 'GET'
}).then((res) => {
if(res.ok){
return res.json();
}else{
throw new Error(res.message);
}
}).then((json) => {
return callback(json);
})
.catch((err) => {
console.log(err);
return callback(err.message);
});
}
Mixing callbacks and promises can make your code a bit messy. I would stick to promises:
app.get('/lights', (req, res) => {
return hue.getLights()
.then(data => {
res.send(data);
})
.catch(err => {
res.status(401).send("An error occured: ", err.message);
});
})
and hue.js
const fetch = require('node-fetch');
const gateway = "192.168.0.12";
const username = "username-A";
function fetchAPI(url, ...rest) {
return fetch(`http://${gateway}/api/${username}${url}`, ...rest);
}
function getLights() {
return fetchAPI(`/lights`)
.then(res => res.json())
.then(json => json.map((light, i) => `ID: ${i} Name: ${light.name}`));
}
function getLightDetails(id) {
return fetchAPI(`/lights/${id}`)
.then(res => res.json());
}
function getLightState(id) {
return fetchAPI(`/lights/${id}`)
.then(res => res.json())
.then(light => `Name: ${light.name} On: ${light.state.on}`);
}
function setLightState(id, state) {
return fetchAPI(`/lights/${id}/state`, {
method: 'PUT',
body: JSON.stringify({"on": state })
}).then(res => res.json());
}
module.exports = { getLights, getLightDetails, getLightState, setLightState };

React - API Call returning correct result but not passing along

I'm performing an API call to Bing Web Search API and running into an error with the response.
Here's the code:
await webSearchApiClient.web.search(searchText).then(result => {
console.log('Results API', result)
return result
}).catch((err) => {
throw err;
})
The issue I'm running into is that the result does come back (the console log 'Results API' prints the expected return values), but the return statement isn't passing the value along. The rest of the code is written to be asynchronous, and when I print the values in the code calling the API function I get this:
Line 1: Results API {"_type": "SearchResponse","queryContext": {"originalQuery":...
Line 2: Returned Results undefined
I've tried setting the result to other variables with no success
I'm using redux as well, here's the code for the dispatch call and the code in the redux action (the second console log is the the redux actions):
const onSearchResults = async () => {
dispatch(getWebResults(searchText))
dispatch(getImageResults(searchText))
}
export const getWebResults = (searchText) => {
return async dispatch => {
const onStart = () => {
dispatch({ type: GET_WEB_RESULTS_STARTED });
}
const onSuccess = (response) => {
dispatch({ type: GET_WEB_RESULTS_SUCCESS, payload: response });
return response;
}
const onError = (error) => {
dispatch({ type: GET_WEB_RESULTS_FAILURE, payload: error });
return error;
}
try {
onStart();
const webResults = await BingWebSearchApi(searchText);
console.log('Returned Results', webResults)
return onSuccess(webResults)
} catch(error) {
return onError(error)
}
}
}
Instead of doing this
await webSearchApiClient.web.search(searchText).then(result => {
console.log('Results API', result)
return result
}).catch((err) => {
throw err;
})
Since the return statement is for the then function scope, you should return the promise like this
return webSearchApiClient.web.search(searchText);
And then in your redux actions do something like
(...)
try {
onStart();
BingWebSearchApi(searchText).then((webResults)=> {
console.log('Returned Results', webResults);
onSuccess(webResults);
});
} catch(error) {
return onError(error)
}

axios doesn't return response and error separately

I have a React component. Inside that component I have a function onFormSubmit that calls function from another component. This other function is making POST request with axios.
I would like to return if POST request is true a response into first function or error if not. What is happening now is that my 'SUCCESS RESPONSE' console.log is always triggered, even then there is an error in axios POST request. If there is an error then just 'ERROR RESPONSE' console.log should be triggered.
From first component
onFormSubmit = () => {
postJobDescriptionQuickApply(this.state, this.props.jobDescription.id)
.then((response) => {
console.log('SUCCESS RESPONSE', response)
})
.catch((error) => {
console.log('ERROR RESPONSE', error)
})
}
From second component
export const postJobDescriptionQuickApply = (easyApplyData, jobId) => apiUrl('easyApply', 'easyApply').then(url => axios
.post(url, {
applicant: {
email: easyApplyData.email,
fullName: `${easyApplyData.firstName} ${easyApplyData.lastName}`,
phoneNumber: easyApplyData.phoneNumber,
resume: easyApplyData.resume,
source: easyApplyData.source,
},
job: {
jobId,
},
})
.then((response) => {
console.log('SUCCESS', response.data.developerMessage)
return response.data.developerMessage
})
.catch((error) => {
// handle error
console.log('ERROR JOB DESCRIPTION', error.response.data.developerMessage)
return error.response.data.developerMessage
})
calling return indicates success, and the .catch function in the calling method wouldn't be triggered. Instead of returning error.response.data.developerMessage use throw instead. This will cause it to be thrown and then caught with the .catch method in the calling function.
Depending on the situation though, it's generally not advisable to catch and rethrow exceptions like that because you lose stack trace etc. You may be better off not catching the error in the lower method and just relying on the calling method to handle the error.
In the
.catch((error) => {
// handle error
console.log('ERROR JOB DESCRIPTION', error.response.data.developerMessage)
return error.response.data.developerMessage
})
replace return statement with throw error
Not use catch and catch on your second component.
To can use then and catch on your first component you need return axios, something as:
export const postJobDescriptionQuickApply = (easyApplyData, jobId, url) => axios
.post(url, {
applicant: {
email: easyApplyData.email,
...
},
job: {
jobId,
},
});
// or using apiUrl
export const postJobDescriptionQuickApply = (easyApplyData, jobId) => apiUrl('easyApply', 'easyApply')
.then(url => axios.post(url, {
applicant: {
email: easyApplyData.email,
fullName: `${easyApplyData.firstName} ${easyApplyData.lastName}`,
phoneNumber: easyApplyData.phoneNumber,
resume: easyApplyData.resume,
source: easyApplyData.source,
},
job: {
jobId,
},
});
Additionally, do not forget to validate the response status in the first component, something as:
onFormSubmit = () => {
postJobDescriptionQuickApply(this.state, this.props.jobDescription.id)
.then((response) => {
if (response.status === 200) {
console.log('SUCCESS RESPONSE', response);
}
})
.catch((error) => {
console.log('ERROR RESPONSE', error)
})
}
I hope, I could help you

Axios ajax, show loading when making ajax request

I'm currently building a vue app and Im using axios. I have a loading icon which i show before making each call and hide after.
Im just wondering if there is a way to do this globally so I dont have to write the show/hide loading icon on every call?
This is the code I have right now:
context.dispatch('loading', true, {root: true});
axios.post(url,data).then((response) => {
// some code
context.dispatch('loading', false, {root: true});
}).catch(function (error) {
// some code
context.dispatch('loading', false, {root: true});color: 'error'});
});
I have seen on the axios docs there are "interceptors" but II dont know if they are at a global level or on each call.
I also saw this post for a jquery solution, not sure how to implement it on vue though:
$('#loading-image').bind('ajaxStart', function(){
$(this).show();
}).bind('ajaxStop', function(){
$(this).hide();
});
I would setup Axios interceptors in the root component's created lifecycle hook (e.g. App.vue):
created() {
axios.interceptors.request.use((config) => {
// trigger 'loading=true' event here
return config;
}, (error) => {
// trigger 'loading=false' event here
return Promise.reject(error);
});
axios.interceptors.response.use((response) => {
// trigger 'loading=false' event here
return response;
}, (error) => {
// trigger 'loading=false' event here
return Promise.reject(error);
});
}
Since you could have multiple concurrent Axios requests, each with different response times, you'd have to track the request count to properly manage the global loading state (increment on each request, decrement when each request resolves, and clear the loading state when count reaches 0):
data() {
return {
refCount: 0,
isLoading: false
}
},
methods: {
setLoading(isLoading) {
if (isLoading) {
this.refCount++;
this.isLoading = true;
} else if (this.refCount > 0) {
this.refCount--;
this.isLoading = (this.refCount > 0);
}
}
}
demo
I think you are on the right path with dispatch event when ajax call start and finish.
The way that I think you can go about it is to intercept the XMLHttpRequest call using axios interceptors like so:
axios.interceptors.request.use(function(config) {
// Do something before request is sent
console.log('Start Ajax Call');
return config;
}, function(error) {
// Do something with request error
console.log('Error');
return Promise.reject(error);
});
axios.interceptors.response.use(function(response) {
// Do something with response data
console.log('Done with Ajax call');
return response;
}, function(error) {
// Do something with response error
console.log('Error fetching the data');
return Promise.reject(error);
});
function getData() {
const url = 'https://jsonplaceholder.typicode.com/posts/1';
axios.get(url).then((data) => console.log('REQUEST DATA'));
}
function failToGetData() {
const url = 'https://bad_url.com';
axios.get(url).then((data) => console.log('REQUEST DATA'));
}
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<button onclick="getData()">Get Data</button>
<button onclick="failToGetData()">Error</button>
For Nuxt with $axios plugin
modules: ['#nuxtjs/axios', ...]
plugins/axios.js
export default ({ app, $axios ,store }) => {
const token = app.$cookies.get("token")
if (token) {
$axios.defaults.headers.common.Authorization = "Token " + token
}
$axios.interceptors.request.use((config) => {
store.commit("SET_DATA", { data:true, id: "loading" });
return config;
}, (error) => {
return Promise.reject(error);
});
$axios.interceptors.response.use((response) => {
store.commit("SET_DATA", { data:false, id: "loading" });
return response;
}, (error) => {
return Promise.reject(error);
})
}
store/index.js
export default {
state: () => ({
loading: false
}),
mutations: {
SET_DATA(state, { id, data }) {
state[id] = data
}
},
actions: {
async nuxtServerInit({ dispatch, commit }, { app, req , redirect }) {
const token = app.$cookies.get("token")
if (token) {
this.$axios.defaults.headers.common.Authorization = "Token " + token
}
let status = await dispatch("authentication/checkUser", { token })
if(!status) redirect('/aut/login')
}
}
}
This example is accompanied by a token check with $axios and store

Pass error after catch on promises

Hi I'm new so sorry if my question does not formulate properly.
I want to define a promise from axios js in a global function.
Here I want to handle / catch the 401 status globally and logout the user.
I do not want to handle it in every single query.
Here my source global function to handle a request:
export function requestData (url, payload = {}) {
return axios.post(url, payload)
.then(response => {
return response.data
})
.catch(error => {
if (error.response.status === 401) {
logout()
} else {
return error
}
})
}
And here a example function I use on a controller:
requestData('/api/persons', {options: this.options, search: search})
.then(data => {
this.data = data
})
.catch(error => {
this.error = error.toString()
})
My Problem is that the promise catch in my controller will not fire when there is an exception. How to realize this?
change return error in your requestData function to throw error
As per the Axios docs
You can intercept requests or responses before they are handled by then or catch.
You're going to want to use the Response Interceptor:
axios.interceptors.response.use(function(response) {
// Do something with response data
return response;
}, function(error) {
// Do something with response error
if (error.status === 401) {
logout()
}
return Promise.reject(error);
});
Replacing return error by throw error is the half work.
When I'm right the throw error in promise catch will not invoke the next promise .catch statement. This will work in the .then statement.
This way it should work:
export function requestData (url, payload = {}) {
return axios.post(url, payload)
.then(response => {
return response.data
})
.catch(error => {
if (error.response.status === 401) {
logout()
} else {
return error
}
})
.then(result => {
if (result instanceof Error) {
throw result
} else {
return result
}
})
}

Categories

Resources