using axios with promise API - reactjs

I am using a promise based hook in a React app to fetch async data from an API.
I am also using a Axios, a promise based http client to call the API.
Is it an anti-pattern to use a promise based client inside another promise? The below code does not seem to work.
const getData = () => {
return new Promise((resolve, reject) => {
const url = "/getData";
axios.get(url)
.then(function(response) {
resolve(response);
})
.catch(function(error) {
reject(error);
});
});
const useAsync = (asyncFunction) => {
const [value, setValue] = useState(null);
const execute = useCallback(() => {
setPending(true);
setValue(null);
setError(null);
return asyncFunction()
.then(response => setValue(response))
.catch(error => setError(error))
.finally(() => setPending(false));
}, [asyncFunction]);
useEffect(() => {
execute();
}, [execute]);
return { execute, pending, value, error };
};
};
const RidesList = () => {
const {
pending,
value,
error,
} = useAsync(getData);

Oh man. I think you have a fundamental misunderstanding about how Promises work.
First, axios already returns a Promise by default. So your whole first function of getData can be reduced to:
const getData = () => {
const url = "/getData"
return axios.get(url)
}
But the meat of your code seems to indicate you want a querable Promise - so you can check the status of it for whatever reason. Here's an example of how you would do it, adapted from this snippet:
function statusPromiseMaker(promise) {
if (promise.isResolved) return promise
let status = {
pending: true,
rejected: false,
fulfilled: false
}
let result = promise.then(
resolvedValue => {
status.fulfilled = true
return resolvedValue
},
rejectedError => {
status.rejected = true
throw rejectedError
}
)
.finally(() => {
status.pending = false
})
result.status = () => status
return result
}
In this way, you can then do something like let thing = statusPromiseMaker(getData()) and if you look up thing.status.pending you'll get true or false etc...
I didn't actually run what's above, I may have forgotten a bracket or two, but hopefully this helps.
I have to admit - I haven't seen anything like this ever used in the wild. I am interested in knowing what you're actually trying to accomplish by this.

Axios itself returns a promise but if you want to make a custom class having your custom logic after each API call then you can use interceptors I was having the same requirement and this is how I am returning promises after applying my custom logic on each API call.
Interceptors will get executed separately after and before each request you made so we can simply use them if we want to modify our request or response.
here is my working solution have a look at it.
callApi = (method, endpoint, params) => {
this.apiHandler.interceptors.request.use((config) => {
config.method = method
config.url = config.baseURL + endpoint
config.params = params
return config
})
return new Promise((resolve, reject) => {
this.apiHandler.interceptors.response.use((config) => {
if (config.status == 200) {
resolve(config.data)
} else {
reject(config.status)
}
// return config
}, error => reject(error))
this.apiHandler()
})
}
Below is the code to call this function
helper.callApi("get", "wo/getAllWorkOrders").then(d => {
console.log(d)
})

Related

React how to wait for all axios to finish

I want to wait until all axios in useEffect are finished.
UseEffect:
useEffect(() => {
async function getHomePageContent() {
await HomePageServices.getSliderContent().then((response) => {
setSliderProduct(response.data);
});
await HomePageServices.getRecommendedProducts().then((response) => {
setRecommendedProducts(response.data);
});
await HomePageServices.getMostOrderProducts().then((response) => {
setMostOrderProducts(response.data);
});
await HomePageServices.getMostRatedProducts().then((response) => {
setMostRatedProducts(response.data);
});
}
getHomePageContent().catch((error) => {
console.log(error)
});
}, []);
Class:
class HomePageServices{
async getSliderContent(){
return await axios.get(baseURL+"/slider")
}
async getMostRatedProducts(){
return await axios.get(baseURL+"/mostRatedProducts")
}
async getMostOrderProducts(){
return await axios.get(baseURL+"/mostOrderProduct")
}
async getRecommendedProducts(){
return await axios.get(baseURL+"/recommendedProduct")
}
}
Can someone explain to me how to wait for all axios to end, and if one failed, how to find out which one it was?
Try using Promise.allSettled() which takes an iterable (e.g. array) of promises and resolves into array of results of each of them.
Results are represented as objects with status key, which can be rejected or fulfilled. The second key of the object is either value containing the resolved value, or reason in case promise was rejected.
Taking this, then your code in useEffect might be something like this:
useEffect(() => {
const getHomePageContent = async () => ({
const promises = [
HomePageServices.getSliderContent(),
HomePageServices.getRecommendedProducts(),
HomePageServices.getMostOrderProducts(),
HomePageServices.getMostRatedProducts()
];
const data = await Promise.allSettled(promises);
const [slider, recommended, mostordered, mostrated] = data;
// result for each of promise
console.log(slider); // { status: 'fulfilled', value: 123 }
console.log(recommended) // { status: 'rejected', reason: 'blah'}
});
getHomePageContent().catch((er) => console.log(er))
}, [])

Why isn't my Promise in a Post response in MockAdapter being evaluated?

When I mock a get using MockAdapter and reply with a Promise, the promise is evaluated fine and I'm able to select the response data.
When I try the same thing with a Post, it doesn't work. The .then method isn't evaluated and I don't get the response data.
test:
var axios = require("axios");
var MockAdapter = require("axios-mock-adapter");
let mock;
beforeAll(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.reset();
});
test("", async () => {
const response = { data: { assignee: '' }};
mock.onPost(`${APP_ROOT}/${ASSIGN}`).reply(function (config) {
return new Promise(function(resolve, reject) {
setTimeout(function () {
resolve([200, response]);
}, 1000);
});
})
})
useEffect in page:
useEffect(() => {
assign(id)
.then(responseData => {
})
.catch(error => {
})
}
I've got a very similar test using mock.onGet returning a Promise and everything works fine. Is there a reason why this shouldn't work for a Post?
I've tried various url's on the Post. For the onGet, I leave it blank but haven't seen any onPost examples with this - is onPost() with no url legal?
In case it matters, I've also set up a localStorage mock.

Return the fetch response from another file

I am trying to call a function that calls fetch to an API from a React component in a separate file and am not finding the correct solution to get the correct response back.
When I debug, the result returns before the updateAccount function has completed and the final result is never returned to my update function.
Inside the fetch, the API returns the correct response whether it is successful or has validation errors and those results are correctly assigned to result.success and result.errors but the result doesn't get returned from the function so that the caller can make use of those values.
Inside of my React component:
import { updateAccount } from '../services/requests';
...
const update = (account: EditAccountModel) => {
const result = updateAccount(account);
if(result.errors.length > 0) {
// will notify of errors
console.log(result.errors); // is an empty array instead of validation errors
} else {
// will notify of success
console.log(result.success); // is an empty string instead of success message
}
}
...
My request file
export const updateAccount = (account: EditAccountModel | undefined): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
}).catch(genericError => {
result.errors.push(genericError);
});
}
});
return result;
}
The result reassignment happens inside then catch but it won’t be affective in the way you expected. The guaranteed way to return correct result is via a callback() passed to your updateAccount() if you could afford it:
export const updateAccount = (
account: EditAccountModel | undefined,
callback: Function
): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
callback(result);
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
callback(result);
}).catch(genericError => {
result.errors.push(genericError);
callback(result);
});
}
});
}
And inside your React component:
const update = (account: EditAccountModel) => {
const handleResult = (res) => {
// your result callback code
// ...
};
updateAccount(account, handleResult);
// ...
}
Alternative way that keeps your current structure is to change your current updateAccount() to an async function, then return await fetch().
You need to wait for the response . I'll let read more about how Promise work in JavaScript.
I wouldn't code updateAccount the same way you did, especially where you use the variable result and update it inside the flow of the promise (you really don't need that). You're also using React so you can use the state to store and update the result of the update function. But let's fix your problem first:
export const updateAccount = async (account: EditAccountModel | undefined): EditAccountResponseModel => {
const result = new EditAccountResponseModel();
await fetch(baseUrl, {
method: 'PUT',
body: JSON.stringify(account),
headers
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
result.success = `${account?.name} was updated successfully!`
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
result.errors.push(jsonError);
}).catch(genericError => {
result.errors.push(genericError);
});
}
});
return result;
}
First make your function updateAccount async then await the result of the promise.
Now the same thing for the function update:
const update = async (account: EditAccountModel) => {
const result = await updateAccount(account);
if(result.errors.length > 0) {
// will notify of errors
} else {
// will notify of success
}
}

React - how to make an api request wait for the value of an undefined variable when submitting a form

I'm trying to make an api request by passing an id parameter in the request url
async function carregarPlanoCurso() {
const response = await api.get(`cursos/${resPesquisaCurso.id}/plano`);
return response.data;
}
The problem is that when calling the carregarPlanoCurso in english "loadCoursePlan()" method the variable resPesquisaCurso "searchCoursesearch" is undefined and I get the following error:
GET http://localhost:3001/cursos/undefined/plano 500 (Internal Server Error)
createError.js:16 Uncaught (in promise) Error: Request failed with status code 500
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:62)
The variable resPesquisaCurso depends on the carregarCursos() method which loads all courses which are then filtered according to id:
const [resPesquisaDisciplina, setResPesquisaDisciplina] = useState([]);
const [resPesquisaCurso, setResPesquisaCurso] = useState([]);
const [resPesquisaPlano, setResPesquisaPlano] = useState([]);
const [resPesquisaEdicao, setResPesquisaEdicao] = useState([]);
const [plan, setPlan] = useState([]);
const user = useSelector((state) => state.user.profile);
async function carregarDisciplinas() {
const response = await api.get('disciplinas/');
return response.data;
}
async function carregarCursos() {
const response = await api.get('cursos/');
return response.data;
}
async function carregarEdicoes() {
const response = await api.get('disciplinas/all/edicoes');
return response.data;
}
async function carregarPlanoCurso() {
const response = await api.get(`cursos/${resPesquisaCurso.id}/plano`);
return response.data;
}
function handleSubmit({
anoLetivo1,
curso1,
unidadeCurricular1,
dataInicio1,
dataFinal1,
}) {
carregarCursos().then((value) => {
setResPesquisaCurso(value.filter((curso) => curso.nome === curso1));
});
carregarDisciplinas().then((value) => {
setResPesquisaDisciplina(
value.filter((disciplina) => disciplina.nome === unidadeCurricular1)
);
});
carregarPlanoCurso().then((value) => {
setPlan(value);
setResPesquisaPlano(
value.filter((plano) => plano.id_disci === resPesquisaDisciplina.id)
);
});
carregarEdicoes().then((value) => {
setResPesquisaEdicao(
value.filter(
(edicao) => edicao.id_disciplina === setResPesquisaDisciplina.id
)
);
});
console.log(resPesquisaPlano);
}
My problem is how to make the method carregarPlanoCurso() wait for the execution of the carregarCursos() method to finish executing for only then I can make sure that the searchCourse search variable is not undefined and make the call to api passing this variable as a parameter in the url:
Problem focus:
carregarCursos().then((value) => {
setResPesquisaCurso(value.filter((curso) => curso.nome === curso1));
});
carregarPlanoCurso().then((value) => {
setResPesquisaPlano(
value.filter((plano) => plano.id_disci === resPesquisaDisciplina.id)
);
});
Maybe you can do this way:
carregarCursos().then((value) => {
setResPesquisaCurso(value.filter((curso) => curso.nome === curso1));
}).then(() => carregarPlanoCurso().then((value) => {
setResPesquisaPlano(
value.filter((plano) => plano.id_disci === resPesquisaDisciplina.id)
);
}));
When you use a promise, JavaScript will not wait for that promise to resolve or reject. It will put this call in another part of the event loop and continue executing your code.
If I could, I want to suggest a change inside your handleSubmit function like this:
function handleSubmit({
anoLetivo1,
curso1,
unidadeCurricular1,
dataInicio1,
dataFinal1,
}) {
try {
const {data: cursos} = await carregarCursos();
setResPesquisaCurso(cursos.filter((curso) => curso.nome === curso1));
const {data: disciplinas} = await carregarDisciplinas();
setResPesquisaDisciplina(disciplinas.filter((disciplina) => disciplina.nome === unidadeCurricular1);
// rest of your code using this approach.
} catch (err) {
// handle with your error
}
}
Doing this way JavaScript will wait for the promise resolves or reject.

how can I return axios data to feed view?

I am building feed news using reactjs, but however, I notice that Axios is not returning data back to my browser feed in order to process the data, what am I missing on this code?
const browserFeed = async () => {
const response = await browse();
console.log(response)
setFeed(response.data.results); // undefined
setIntialized(true);
};
export const browse = () => {
axios.get('xxxxxxxxxxx').then((result) => {
return result // undefined
}).catch((error) => {
return error;
})
}
You are missing returning the Promise axios.get returns.
export const browse = () => {
return axios.get('xxxxxxxxxxx').then((result) => {
return result;
}).catch((error) => {
return error;
});
};
You can simply return the Promise though, you don't need to chain another .then just to return the resolved value.
export const browse = () => {
return axios.get('xxxxxxxxxxx').catch((error) => {
return error;
});
};
And to be honest I don't think you want to catch and return the error here either since your component logic is written to assume success. You can return the Promise and handle any rejected promises in a try/catch.
export const browse = () => {
return axios.get('xxxxxxxxxxx');
};
But this is a little boring, may as well just make the axios call in the component. surround all the fetching logic in a try/catch, set the feed in the happy success branch, set any error state in the sad failure branch, and set the initialized state in finally block so no matter pass/fail you can indicate data was fetched.
const browserFeed = async () => {
try {
const response = await axios.get('xxxxxxxxxxx');
console.log(response)
setFeed(response.data.results);
} catch(error) {
// set any error state
} finally {
setInitialized(true);
}
};
This avoids the mixing of the async/await syntax with any promise-chain syntax, which should be avoided.

Resources