I´m new to react. I´m trying to fetch an endpoints array. and I want to update the api's status every 15 seconds. I´m trying to do this
const [data, setData] = useState<Response[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string[] | null[]>([]);
const effectRan = useRef(false);
const fetching = async () => {
setLoading(true);
endpoints.map(async (endpoint) => {
return await axios
.get(endpoint)
.then((res) => {
setData((prev) => [...prev, res.data]);
})
.catch((err) => {
setError([...error, err.message]);
});
});
setLoading(false);
};
useEffect(() => {
if (!effectRan.current) {
fetching();
}
return () => {
effectRan.current = true;
};
});
useEffect(() => {
setTimeout(async () => {
setData([]);
setLoading(true);
setError([]);
await fetching();
}, 15000);
}, []);
but when the seTimeout runs every card duplicates and the state gets more data than before. even though I´m reseting the state to setData([])
I just want to update the api's status. What can i do?
This is the rest of my code
if (loading) return <Spinner />;
return (
<div className="card-container">
{data.length ? (
data.map((item) => {
return (
<Card
key={generateKey()}
hostname={item.hostname}
message={item.message}
success={item.success}
time={item.time}
/>
);
})
) : (
<Spinner />
)}
{error.length
? error.map((err) => (
<ErrorCard key={generateKey()} message={err as string} />
))
: null}
</div>
```
CodePudding user response:
I think this piece of code might be adding additional data instead of overwriting the existing one. Is that what you're trying to do?
setData((prev) => [...prev, res.data]);
CodePudding user response:
Theres a few things wrong here and one or more probably fixes it:
- You keep a ref around to track the first fetch but theres no need as you can do that by virtue of using
[]
in an effects deps array, which you already have. - The loading state does not wait until all requests in flight finished.
- The 15 second interval does not wait until all requests launched are finished.
- You dont clear down the timer if the component unmounts and remounts.
Your code, by design it seems, does append data each time one of the requests comes back -- but I think that was intentional?
const [data, setData] = useState<Response[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string[] | null[]>([]);
const fetching = async () => {
setLoading(true);
await Promise.all(
endpoints.map((endpoint) => {
return axios
.get(endpoint)
.then((res) => {
setData((prev) => [...prev, res.data]);
})
.catch((err) => {
setError([...error, err.message]);
});
})
);
setLoading(false);
};
useEffect(() => {
let timer: number | null = null;
const intervalFetch = async () => {
await fetching();
timer = setTimeout(async () => {
setError([]);
setData([]);
intervalFetch();
}, 15000);
};
intervalFetch();
return () => timer !== null && clearTimeout(timer);
}, []);