useEffect w/ Async Function
Async Function in useEffect
When you have async function like:
async () => { try { const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`); const json = await response.json(); setPosts(json.data.children.map(it => it.data)); } catch (e) { console.error(e); } }
the try
block would implcitly return a promise.
useEffect
only expects a cleanup function as a return.
You have to be explicit about calling the async function:
useEffect(() => { async function fetchData() { try { const response = await fetch( `https://www.reddit.com/r/${subreddit}.json` ); const json = await response.json(); setPosts(json.data.children.map(it => it.data)); } catch (e) { console.error(e); } }; fetchData(); }, []);
You can also use IIFE:
useEffect(() => { (async function() { try { const response = await fetch( `https://www.reddit.com/r/${subreddit}.json` ); const json = await response.json(); setPosts(json.data.children.map(it => it.data)); } catch (e) { console.error(e); } })(); }, []);
Other Caveats
Do NOT extract the async function out.
- It is NOT clear to the user whether the called async function has a cleanup function attached to it or not.
- If the cleanup function is needed, it will lead to a memory leak.
Controlling the Render
Using setTimeOut
, you can control the rendering behavior of the component.
- this would come useful when a large amount of data needs to be fetched or screen needs to be coordinated while the context state is updating.
useEffect(() => { setIsLogingLoading(true); const fetchData = async () => { const data = fetch('url'); const result = await data.json(); setTimeout(() => { setIsLoginLoading(false); }, 1000); setSomeState(result); } fetchData(); }, []);
You can also chain two useEffect
to use the result of one effect in the next:
useEffect(() => { setIsLoginLoading(true); const fetchData = async () => { const data = fetch('url'); const result = await data.json(); setTimeout(() => { setIsLoginLoading(false); }, 1000); // coordindated load/redirect time setSomeState(result); } fetchData(); }, []); useEffect(() => { if(isLoginLoading) { setTimeOut(() => { router.push('/') }, 1000) // coordindated load/redirect time } })