Why is useEffect called twice in ReactJS
This is a question I’ve been looking for for a while and can’t find any information today, so I accidentally read it, so I’m sharing it for everyone who’s looking for the same thing.
1. Use case
- If we are familiar with ReactJS, call the API in useEffect as follows:
1 2 3 4 | useEffect(()=>{ api.post("/view",{}) },[]) |
- But when you check in the network, you will see this API being called twice. So where is the cause?
2. Explanation
- The exact cause is due to Strict mode in Development mode and once you build, you won’t get this error anymore.
1 2 3 4 5 6 7 | ReactDOM.render( <React.StrictMode> {app} </React.StrictMode>, document.getElementById('root') ); |
3. Reason
- In DEV mode, this is just a process to help you check for errors that may arise during API calls and promptly issue warnings if possible.
- When in product mode you won’t have this problem anymore
4. How to fix it
- Turn off StrictMode
- Use the useRef hook to customize useEffect as follows. You will have a usEffectOnce hook in DEV mode.
1 2 3 4 5 6 7 8 9 10 11 12 | export default function useEffectOnce(fn: () => void) { const ref = useRef(false); useEffect(() => { if (ref.current) { fn(); } return () => { ref.current = true; }; }, [fn]); } |
5. Another alternative when calling API
- As you know, useEffect will be run when the entire page or commponent is completely rendered. So when we put the API inside it, the API will have to wait for a while before being run. This is not optimal. Alternative: you can use useQuery hook to run these 2 parts in parallel. For example:
1 2 3 4 5 6 7 8 9 | const { status, data, error, isFetching } = useQuery( ['data'], async () => { const data = await ( await fetch(`${API_BASE_URL}/data`) ).json() return data } ) |