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()
Related
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;
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
);
I would like to implement Axios cancel token through useEffect cleanup code, I can implement this way when calling the axios in the component, however I would like to know if I can implement cancelToken when axios call is in separate file.
[when using axios in the component]
useEffect(() => {
const request = Axios.CancelToken.source()
const fetchPost = async () => {
try {
const response = await Axios.get(`endpointURL`, {
cancelToken: request.token,
})
setPost(response.data)
setIsLoading(false)
} catch (err) {
console.log('There was a problem or request was cancelled.')
}
}
fetchPost()
return () => request.cancel()
}, [])
[page component]
useEffect(() => {
const cancel = axios.CancelToken.source();
fetchData();
return () => {
};
}, []);
const fetchData= async () => {
try {
const payload = {
page: 0,
size: 10,
};
const {
data: { content },
} = await UserService.getUserSupplier(payload);
setSupplier(content);
} catch (err) {
console.log(err);
}
};
[UserService.js]
export const getUserSupplier = async payload => {
const url = `/user/supplier?${qs.stringify(payload)}`;
const response = await ApiService.get(url); // ApiService is just Axios I separated file because of interceptor
return response;
};
I am writing a custom react hook for fetching data from an endpoint. This is what the function looks like
import { useState } from "react";
const useHttp = async (endpoint, method, data) => {
const [loading, setLoading] = useState(false)
const [fetchedData, setfetchedData] = useState(null)
setfetchedData(await fetch.method(endpoint));
return [isLoading, fetchedData]
}
export default useHttp;
As you can see, I want to do a fetch request to whatever method is passed on to the useHttp hook. Please someone point me how to do it?
You cannot pass async functions to React Hooks. You have to useEffect
import { useState, useEffect } from "react";
const useHttp = (endpoint, method, options) => {
const [isLoading, setLoading] = useState(false);
const [fetchedData, setFetchedData] = useState(null);
useEffect(() => {
setLoading(true);
fetch(endpoint, { method, ...options })
.then(data => data.json())
.then((json) => {
// do something with JSON data
setFetchedData(json);
})
.catch((err) => {
// do something with err
})
.finally(() => {
setLoading(false);
});
}, []);
return [isLoading, fetchedData];
};
export default useHttp;
Use useEffect hook to make the HTTP request.
fetch function takes an optional second argument which is an object specifying various options for the HTTP request and one of the options is a method option. Use this method option to specify the request method.
import { useState, useEffect } from "react";
const useHttp = async (endpoint, method, data) => {
const [loading, setLoading] = useState(false);
const [fetchedData, setfetchedData] = useState(null);
useEffect(() => {
setLoading(true);
fetch(endpoint, { method })
.then(res => res.json())
.then(data => {
setLoading(false);
setfetchedData(data);
})
.catch(err => {
setLoading(false);
console.log(err.message);
});
}, []);
return [isLoading, fetchedData];
}
For details on how to specify options for fetch function and different options that can be specified, see using fetch
If you want to use async-await syntax, you can write useEffect hook as:
useEffect(() => {
async function makeRequest() {
setLoading(true);
try {
const response = await fetch(endpoint, { method });
const data = await res.json();
setLoading(false);
setfetchedData(data);
} catch (error) {
setLoading(false);
console.log(err.message);
}
}
makeRequest();
}, []);
hi maybe this help you:
1- call function:
const useHttp = async (url,method,data)=>{
var options = {
method:method,
headers: {
'Content-Type': 'application/json; charset=utf-8;'
}
};
if(method==='POST' && data)
options.body = JSON.stringify(data);
const response = await fetch(url, options);
const rep = await response.json();
console.log(rep);
return rep;
};
in above code first create your request options and then send it by fetch to end point.
2- use it in compoent like below:
setLoading(true);
var rep = await useHttp(...)
setLoading(false);
So basically, I'm trying to fetch data from api and pass it to Component.
I create usePosition hook to get my positon from browser, and then get response from api. I really don't know how to wait with useEffect for my position, when i'm executing this code now I'm getting always log 'no position'.
const usePosition = () => {
const [error, setError] = useState(null);
const [position, setPosition] = useState();
useEffect(() => {
const geo = navigator.geolocation;
if(!geo) {
setError('Geolocation is not supported.');
return;
}
const handleSuccess = position => {
const { latitude, longitude } = position.coords;
setPosition({
latitude,
longitude
});
};
const handleError = error => {
setError(error.message);
};
geo.getCurrentPosition(handleSuccess, handleError);
}, []);
return { position, error };
}
function App() {
const {position, error} = usePositon();
const [weather, setWeather] = useState([]);
useEffect(() => {
if(position) {
const URL = `https://api.openweathermap.org/data/2.5/onecall?lat=${position.latitude}&lon=${position.longitude}&exclude=current,minutely,daily&units=metric&lang=pl&appid=${API_KEY}`;
const fetchData = async () => {
const result = await fetch(URL)
.then(res => res.json())
.then(data => data);
setWeather(result.hourly);
}
fetchData();
} else {
console.log('no position');
}
}, []);
return (
<div className="App">
<div>
<Swiper weather={weather}/>
</div>
</div>
)
}
It's all because of [] empty dependencies list down in App's useEffect. It runs exactly once on mount, when usePosition has not requested anything yet. And once it successes later and returns different { error, position } App does not react.
How to solve? Provide things as dependencies:
useEffect(() => {
if(position) {
const URL = `https://api.openweathermap.org/data/2.5/onecall?lat=${position.latitude}&lon=${position.longitude}&exclude=current,minutely,daily&units=metric&lang=pl&appid=${API_KEY}`;
const fetchData = async () => {
const result = await fetch(URL)
.then(res => res.json())
.then(data => data);
setWeather(result.hourly);
}
fetchData();
} else {
console.log('no position');
}
}, [position, error]);