How can i stop axios request? - reactjs

I'm trying to stop axios request. I use useInterval(custom hooks)(I referenced a website) to request api.
So I stop it with useState and it's totally stopped when i set interval like 1000ms.
however, when i set interval like 100ms then i can't stop api request. it's stopped after 3seconds or something.
So i tried to use if statement. but it's not working as i expected.
and I also checked Network from development tool on chrome
and the request Status was getting changed from pending to 200
and when all the request's Status change to 200, then it stopped.
I really want to know how i can stop API request properly.
my code is like this
useInterval
import { useEffect } from "react";
import { useRef } from "react";
const useInterval = (callback, delay) => {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const tick = () => {
savedCallback.current();
};
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
};
export default useInterval;
API calling
const [API_DATA, setAPI_DATA] = useState(null);
const [apiStart, setApiStart] = useState(false);
const [spinner, setSpinner] = useState(false);
//Request API
const getAPI = useCallback(async () => {
if (apiStart) {
await axios
.get(API_URL, {
headers: Header,
})
.then(response => {
setAPI_DATA(response.data);
setSpinner(false);
})
.catch(error => {
init();
console.log("error");
});
}
}, [API_DATA, spinner]);
// start API
const start_API = () => {
setSpinner(true);
setApiStart(true);
};
//stop API
const stop_API = () => {
setSpinner(false);
alert("API STOP");
setApiStart(false);
};
//using useInterval
useInterval(
() => {
if (apiStart) return getAPI();
},
apiStart ? 100 : null
);

Related

How to keep calling api until task is completed in reactjs?

I am working on reactjs as frontend, and Django in the backend.
I have a time taking task written in django, which ideally takes more time to retrieve values than the stipulated API response time.
Therefore, I have made it into a celery task, whose task id I return as an API response.
The plan was to make the API call on page load which starts the celery task, and returns the task ID. So, with the task ID, I can keep polling another API to get the task's status, until completed. Once the task is completed, I can ping another API to get the response of the celery task.
I thought, I can make the API call, and thenafter run a loop with a sleep, but not sure how to achieve this?
import { useEffect, useState } from "react"
import axios from "axios"
function App() {
const [taskId, setTaskId] = useState("")
const apiToSpawnTask = () => {
axios.get("http://localhost:8000/spawn_task")
.then(({data}) => setTaskId(data.task_id))
}
const checkTaskStatus = () => {
axios.get(`http://localhost:8000/task-id/${taskId}`)
.then(({data}) => {
// data.status contains the status of the task id
})
}
const getCompletedTaskResult = () => {
axios.get(`http://localhost:8000/get-task-result/${taskId}`)
.then(({data}) => {
// this data is used in the return
})
}
useEffect(() => {
// What should be the code here?
})
return (<div>Test</div>)
}
const checkTaskStatus = () => {
return axios.get(`http://localhost:8000/task-id/${taskId}`)
.then(({data}) => {
return data
})
}
// ...
useEffect(() => {
const interval = setInterval(() => {
const status = checkTaskStatus
// probably a different property returned from your api
if (status.ready) {
clearInterval(interval)
}
}, 1000)
return () => clearInterval(interval)
})
import { useEffect, useState, useRef } from "react"
import axios from "axios"
function App() {
const [taskId, setTaskId] = useState("");
const intervalRef = useRef(null);
const apiToSpawnTask = () => {
axios.get("http://localhost:8000/spawn_task")
.then(({data}) => {
setTaskId(data.task_id);
intervalRef.current = setInterval(() => {
checkTaskStatus(data.task_id);
}, 5000);
})
}
const checkTaskStatus = (id) => {
axios.get(`http://localhost:8000/task-id/${id}`)
.then(({data}) => {
// data.status contains the status of the task id
if(data.status === 'success') {
getCompletedTaskResult();
clearInterval(intervalRef.current);
}
})
}
const getCompletedTaskResult = () => {
axios.get(`http://localhost:8000/get-task-result/${taskId}`)
.then(({data}) => {
// this data is used in the return
})
}
useEffect(() => {
apiToSpawnTask();
}, [])
return (<div>Test</div>)
}
Tip: instead of hardcoding the base URL as http://localhost:8000, try to use axios instance for that. So if you want to change base URL in future, you don't need to modify everywhere.
axios/index.js
import axios from "axios";
const instance = axios.create({
baseURL: "http://localhost:8000"
});
export default instance;

Stop axios request call in react

I'm trying to stop axios request.
I use useInterval(custom hooks)(I referenced a website) to request api.
So I stop it with useState and it's totally stopped when i set interval like 1000ms.
however, when i set interval like 100ms then i can't stop api request. it's stopped after 3seconds or something.
So i tried to use if statement. but it's not working as i expected.
and I also checked Network from development tool on chrome
and the request Status was getting changed from pending to 200
and when all the request's Status change to 200, then it stopped.
I really want to know how i can stop API request properly.
my code is like this
useInterval
import { useEffect } from "react";
import { useRef } from "react";
const useInterval = (callback, delay) => {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const tick = () => {
savedCallback.current();
};
if (delay !== null) {
const id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
};
export default useInterval;
API calling
const [API_DATA, setAPI_DATA] = useState(null);
const [apiStart, setApiStart] = useState(false);
const [spinner, setSpinner] = useState(false);
//Request API
const getAPI = useCallback(async () => {
if (apiStart) {
await axios
.get(API_URL, {
headers: Header,
})
.then(response => {
setAPI_DATA(response.data);
setSpinner(false);
})
.catch(error => {
init();
console.log("error");
});
}
}, [API_DATA, spinner]);
// start API
const start_API = () => {
setSpinner(true);
setApiStart(true);
};
//stop API
const stop_API = () => {
setSpinner(false);
alert("API STOP");
setApiStart(false);
};
//using useInterval
useInterval(
() => {
if (apiStart) return getAPI();
},
apiStart ? 100 : null
);
Go take a look at the axios documentation at https://axios-http.com/docs/cancellation. I would remove the if(apiStart) as this does not do much. I would possibly rewrite your this method as follows:
const [data, setData] = useState(null);
const [spinnerActive, setSpinnerActive] = useState(false);
const controller = new AbortController();
const getAPI = useCallback(async () => {
setSpinnerActive(true);
await axios
.get(API_URL, {
headers: Header,
signal: controller.signal
})
.then(response => {
setData(response.data);
setSpinnerActive(false);
})
.catch(error => {
setSpinnerActive(false);
console.log("error");
});
}, [data, spinnerActive]);
useInterval(
() => {
getApi()
},
apiStart ? 100 : null
);
Then when you want to abort the request, call controller.abort()

Combine two Axios calls inside one useEffect with async/await

I'm currently trying to understand how to work with async/await in React JS. In this demo app, I'm trying to get full border names of the chosen country by calling https://restcountries.eu/. I make first API call to get info about country and the second one to get full name of its borders since first API call returns
only short border names. I believe there is a way to combine those calls inside one useEffect however everything I tried gave me some sort of an error or getting stuck in infinite loop. What is the proper way to combine those calls with async/await approach?
import React, { useState, useEffect } from "react";
import Axios from "axios";
const App = () => {
const [loading, setLoading] = useState(true);
const [country, setCountry] = useState({});
const [fullBorderNames, setFullBorderNames] = useState([]);
//FIRST API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
const fetchData = async () => {
setLoading(true);
try {
const response = await Axios(
`https://restcountries.eu/rest/v2/name/canada?fullText=true`,
{ cancelToken: source.token }
);
setCountry(response.data[0]);
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
};
fetchData();
return () => source.cancel();
}, []);
//SECOND API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
let borders = [];
if (country.borders) {
const fetchData = async () => {
try {
country.borders.forEach(async border => {
const response = await Axios(
`https://restcountries.eu/rest/v2/alpha?codes=${border}`,
{ cancelToken: source.token }
);
borders.push(response.data[0].name);
if (borders.length === country.borders.length)
setFullBorderNames(borders);
});
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
setLoading(false);
};
fetchData();
}
return () => source.cancel();
}, [country.borders]);
if (loading) {
return <h2>Loading</h2>;
} else {
return (
<>
<pre>{JSON.stringify(country, null, 2)}</pre>
<pre>{JSON.stringify(fullBorderNames, null, 2)}</pre>
</>
);
}
};
export default App;
You can simply just make the requests right after the first one.
try {
const response = await Axios(`https://restcountries.eu/rest/v2/name/canada?
fullText=true`, { cancelToken: source.token });
const country = response.data[0];
setCountry(country);
/* all the other fetch calls*/
Can you tell me what kind of errors you get because I don't see an issue with doing them in the same useEffect? It just gets a little messy which can be refactored anyway.

Best way to refresh and refetch api on a react app

i'm building a simple react app that fetches the data from the OpenWeather Api. i want to refresh the data received every minute to reflect the changes(if there are any changes) to the app. I tried using setInterval when i call the fetchApi function that i created, but according to the console log it doesn't sound very precise or realiable. This is my part of the code:
useEffect(() => {
const currentData = async () => {
const currentWeatherData = await fetchCurrentData();
setCurrentWeather(currentWeatherData);
};
const futureData = async () => {
setFutureWeather(await fetchFutureData())
console.log(futureWeather);
};
currentData();
futureData();
setInterval(() => {
currentData();
futureData();
console.log("reloaded!");
}, 60000);
}, []);
How can i improve this code to make it effectively work?
Thanks guys
I guess that you will need to clear the interval in the cleanup of the useEffect function.
useEffect(() => {
const currentData = async () => {
const currentWeatherData = await fetchCurrentData();
setCurrentWeather(currentWeatherData);
};
const futureData = async () => {
const futureWeatherData = await fetchFutureData();
setFutureWeather(futureWeatherData);
};
currentData();
futureData();
const intervalId = setInterval(() => {
currentData();
futureData();
}, 60000);
return () => {
clearInterval(intervalId);
};
}, []);

Cancel Axios post request outside useEffect hook

GET requests canceling fine in this example:
export default function Post (props) {
const _cancelToken = axios.CancelToken.source()
useEffect(() => {
const _loadAsyncData = async () => {
await axios.get('/post'), { cancelToken: _cancelToken.token })
}
_loadAsyncData()
return () => {
_cancelToken.cancel()
}
}, [])
return ()
}
But when I need save form via POST request, my code looks like:
export default function Form (props) {
const _cancelToken = axios.CancelToken.source()
const _zz = { qq: 'QQ' }
const handleCreate = async e => {
e.preventDefault()
_zz.qq = 'ZZ'
await axios.post('/form'), {}, { cancelToken: _cancelToken.token })
}
useEffect(() => {
return () => {
console.log(_zz.qq)
_cancelToken.cancel()
}
}, [])
return ()
}
Request not cancel and my _zz.qq always 'QQ' instead 'ZZ'. It's working fine without hooks, but I like hooks and want to use hooks for new components.
I want to cancel request when componentWillUnmount.
This is because you're losing the changes between renders. During the handleCreate call the variable changes only for that render. When the useEffect is run on a subsequent render/unmounting, you're resetting _zz to { qq: 'QQ' }. In order to get around this you need to use references.
export default function Form (props) {
const cancelToken = useRef(null)
const zz = useRef({ qq: 'QQ' })
const handleCreate = async e => {
e.preventDefault()
cancelToken.current = axios.CancelToken.source()
zz.current = { qq: 'ZZ' }
await axios.post('/form'), {}, { cancelToken: cancelToken.current.token })
}
useEffect(() => {
return () => {
console.log(zz.current) //this should now be {qq : 'ZZ'}
if (cancelToken.current) {
cancelToken.current.cancel()
}
}
}, [])
return null
}

Resources