There is the following situation - when a certain page is opened, several requests are made through useEffect. These requests go in parallel to each other and are used by the interceptor. In case of an error on request, we render it with a pop-up window using react-toastify. All is well until a certain access error arrives. In this case, 3 windows are rendered with the same error, since the requests were sent at the same time. The task is to visualize this error only once, instead of 3. At the same time, it is necessary to maintain the asynchrony of requests. How could such a thing be implemented? Or maybe I don’t need to touch the requests themselves, but I need to think over the architecture for showing errors? I will be glad to any advice!
Below is a code example.
const instance = axios.create({
baseURL: BASE_URL,
headers: {
"Content-Type": "application/json",
"Access-Control-Expose-Headers": "Content-Disposition",
},
timeout: 3600000,
});
instance.interceptors.request.use(
(config) => {
const configDes = { ...config };
const token = 12345678;
if (token) {
configDes.headers.Authorization = `Bearer ${token}`;
}
return configDes;
},
(error) => Promise.reject(error)
);
instance.interceptors.response.use(
(response) => response,
(error) => Promise.reject(error)
);
const api1 = () => instance.get("api/api1");
const api2 = () => instance.get("api/api2");
const api3 = () => instance.get("api/api2");
const api1Action = () =>
api1()
.then((res) => console.log(res))
.catch((err) => notifyError(err));
const api2Action = () =>
api2()
.then((res) => console.log(res))
.catch((err) => notifyError(err));
const api3Action = () =>
api3()
.then((res) => console.log(res))
.catch((err) => notifyError(err));
const Component = () => {
useEffect(() => {
dispatch(api1Action());
dispatch(api2Action());
dispatch(api3Action());
}, []);
// ToastContainer show notifyError
return (
<ToastContainer /> )
}
I tried to think over an option related to saving the status of the error, but I realized that this would bring problems in the future, because suddenly, immediately after receiving an access error, once the user tries to go somewhere else where there will be the same error. In this case, the error will not be rendered since I saved it before and track this error so as not to show the error window.
CodePudding user response:
Please use Promise.all
when you invoke multiple async requests (assuming you would like to stop if even one request fails)
const api1 = () => instance.get("api/api1");
const api2 = () => instance.get("api/api2");
const api3 = () => instance.get("api/api2");
const Component = () => {
useEffect(() => {
dispatch(Promise.all([ api1(), api2(), api3() ]).then(results = > {
// so whatever needed - results is an array of responses
}).catch(notifyError));
}, []);
return (
<ToastContainer /> )
}
More about Promise.all
and Promise.allSettled
can be found here
EDIT (after more details shared in comment):
const Component = () => {
useEffect(() => {
const apiCalls = [
instance.get("api/api1"),
instance.get("api/api2"),
instance.get("api/api2"),
];
dispatch(Promise.allSettled(apiCalls).then(results = > {
const errors = results.filter(result => result.status == 'rejected').map(result => result.reason);
// decide here if one of the errors break your logic
const values = results.filter(result => result.status == 'fulfilled').map(result => result.value);
// if all ok - use values
}));
}, []);
return (
<ToastContainer /> )
}
CodePudding user response:
The react-toastify library allows you to set a toast element with a toastId value. If two containers have the same ID, then only one container will appear. The library itself keeps track of duplicates of its containers.
const notifyError = (errorMessage: string | Error) =>
toast.error(errorMessage, {
position: "bottom-center",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
toastId: errorMessage as string,
});