I'm trying to find a proper way to "clear" my first fetch request if a second one is made.
I have this:
const [data, setData] = useState([]);
const callAPI = async () => {
const api = await fetch(
`my API`
);
const data = await api.json();
setData(data);
The user can call the same api again to request similar or different date for a second, third, fourth time.... and I'm having issues with rendering information after the first call. I receive the info from the fetch request but React won't load the info properly or at all. I'm wondering if there is a proper way to do something like this:
const [data, setData] = useState([]);
const callAPI = async () => {
** clearFetch() **
const api = await fetch(
`my API`
);
const data = await api.json();
setData(data);
CodePudding user response:
Yes, there is. The fetch()
function can be canceled. You use an AbortController
's signal
object. The details can be seen in this answer.
I will assume you have your call to fetch()
inside a useEffect()
.
let abortC = null;
export default MyComponent = () => {
useEffect(() => {
abortC?.abort();
abortC = new AbortController();
var x = async () => {
try {
var response = await fetch('blah', { signal: abortC.signal });
setSomeState(await response.json());
}
catch (ex) {
// Whatever.
}
};
x();
}, [dependencies]);
return ...
}
UPDATE: Edited to take the declaration of abortC
out to the top level so it is properly enclosed. This would be in response of @EndlessCodebase's comment. Many thanks for the observation.
CodePudding user response:
you can use useEffect's cleanup function to clear previous call. In below code snippet api call will be ignored if add-dependency-which-causes-to-rerun
changes within 500ms. Hope it helps!
useEffect(() => {
const getData = setTimeout(async () => {
const api = await fetch(
'my API'
);
const data = await api.json();
setData(data);
},500)
return () => clearTimeout(getData)
}, [<add-dependency-which-causes-to-rerun>])
CodePudding user response:
if we are fetching data only on button click then above solution may NOT be a good option. I think you need something like this.
import React, { useEffect, useState, useRef } from 'react';
const FetchDetails = () => {
const [inputIsChanged, setTnputIsChanged] = useState(false);
const [prevInput, setPrevInput] = useState('');
const inputRef = useRef('');
const initialRender = useRef(true);
const clickHander = () => {
const currentValue = inputRef.current.value;
setTnputIsChanged(inputRef.current.value !== prevInput);
setPrevInput((prevState) =>
prevState === currentValue ? prevState : currentValue
);
};
useEffect(() => {
(async () => {
if (initialRender.current || !inputIsChanged) {
initialRender.current = false;
return;
}
const response = await fetch(
'https://jsonplaceholder.typicode.com/todos/1'
);
const data = await response.json();
console.log(data);
})();
}, [inputIsChanged]);
return (
<>
<input type="text" ref={inputRef} />
<button onClick={clickHander}>Fetch Details</button>
</>
);
};
export default FetchDetails;