I have a grid with different react ponents, all independant - in that they fetch their own data and display it.
I wanted to somehow make them automatically refetch and update every 15 minutes. My first idea was a HOC, but with the newer hooks and functional ponents I tried something using hooks.
So I already was able to create a custom hook - to handle the fetching of data:
const useFetching = (url) => {
const [loading, setLoading] = useState(true);
const [data, setData] = useState({});
const [error, setError] = useState(null);
const [reload, setReload] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Something went wrong');
}
const json = await response.json();
if (json) {
setData(json);
}
} catch (error) {
setError(error);
}
setLoading(false);
}
fetchData();
}, [url, reload]);
return {
loading,
data,
error
};
};
Then two ponents using this:
const App1 = () => {
const {loading, data, error} = useFetching(
""
);
if (loading || error) {
return <p>{loading ? "Loading..." : error.message}</p>;
}
return (
<ul>
{data.hits.map(hit =>
<li key={hit.objectID}>
<a href={hit.url}>{hit.title}</a>
</li>
)}
</ul>
);
};
const App2 = () => {
const {loading, data, error} = useFetching(
""
);
if (loading || error) {
return <p>{loading ? "Loading..." : error.message}</p>;
}
return (
<ul>
{data.hits.map(hit =>
<li key={hit.objectID}>
<a href={hit.url}>{hit.title}</a>
</li>
)}
</ul>
);
};
ReactDOM.render(<><App1 /><App2/></>, document.getElementById('root'));
This works fine - using the custom hook. So I was thinking something in the lines of:
Set up an interval timer which runs ever 15 minutes and changes the 'reload' state flag - to trigger a refetch and update. Also make sure the interval timer is unset on ponent unload.
To access the state and the setReload method - I placed the timer inside of the useFetching ponent - but that didn't work as it fired up multiple timers. I think I had 14 timers for only two ponents.
I'm really unsure of how to proceed - I would like one timer only handling the refresh of all ponents - at the same time I want the ponents to be independant and not have to share anything with an external timer. It would be fine with one timer per ponent - as long as it properly destroyed when the ponent is unloaded - and it would be nice if this mechanism is built into the useFetching hook.
Is there a much better way to build this - that I'm not thinking of?
Suggestions much appreciated!
I have a grid with different react ponents, all independant - in that they fetch their own data and display it.
I wanted to somehow make them automatically refetch and update every 15 minutes. My first idea was a HOC, but with the newer hooks and functional ponents I tried something using hooks.
So I already was able to create a custom hook - to handle the fetching of data:
const useFetching = (url) => {
const [loading, setLoading] = useState(true);
const [data, setData] = useState({});
const [error, setError] = useState(null);
const [reload, setReload] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Something went wrong');
}
const json = await response.json();
if (json) {
setData(json);
}
} catch (error) {
setError(error);
}
setLoading(false);
}
fetchData();
}, [url, reload]);
return {
loading,
data,
error
};
};
Then two ponents using this:
const App1 = () => {
const {loading, data, error} = useFetching(
"https://hn.algolia./api/v1/search?query=redux"
);
if (loading || error) {
return <p>{loading ? "Loading..." : error.message}</p>;
}
return (
<ul>
{data.hits.map(hit =>
<li key={hit.objectID}>
<a href={hit.url}>{hit.title}</a>
</li>
)}
</ul>
);
};
const App2 = () => {
const {loading, data, error} = useFetching(
"https://hn.algolia./api/v1/search?query=balls"
);
if (loading || error) {
return <p>{loading ? "Loading..." : error.message}</p>;
}
return (
<ul>
{data.hits.map(hit =>
<li key={hit.objectID}>
<a href={hit.url}>{hit.title}</a>
</li>
)}
</ul>
);
};
ReactDOM.render(<><App1 /><App2/></>, document.getElementById('root'));
This works fine - using the custom hook. So I was thinking something in the lines of:
Set up an interval timer which runs ever 15 minutes and changes the 'reload' state flag - to trigger a refetch and update. Also make sure the interval timer is unset on ponent unload.
To access the state and the setReload method - I placed the timer inside of the useFetching ponent - but that didn't work as it fired up multiple timers. I think I had 14 timers for only two ponents.
I'm really unsure of how to proceed - I would like one timer only handling the refresh of all ponents - at the same time I want the ponents to be independant and not have to share anything with an external timer. It would be fine with one timer per ponent - as long as it properly destroyed when the ponent is unloaded - and it would be nice if this mechanism is built into the useFetching hook.
Is there a much better way to build this - that I'm not thinking of?
Suggestions much appreciated!
Share Improve this question edited Mar 18, 2022 at 9:20 VLAZ 29.1k9 gold badges62 silver badges84 bronze badges asked Dec 2, 2019 at 13:11 YonderYonder 7393 gold badges14 silver badges28 bronze badges2 Answers
Reset to default 7You need a useInterval
hook. This post explains exactly why and how: https://overreacted.io/making-setinterval-declarative-with-react-hooks/
Since useFetch
encapsulates the fetching logic for a single ponent, makes sense that each ponent manages it's own interval. You can achieve that by declaring on mount an interval which will be triggered fifteen minutes later.
useEffect(() =>{
let interval = setInterval(() => setReload(true), (1000 * 60 * 15))
//destroy interval on unmount
return () => clearInterval(interval)
})
More about useEffect
(including effect cleanup as above) in the documentation.