Make React useEffect hook not run on initial render
We can use the useRef
hook to store any mutable value we like, so we could use that to keep track of if it's the first time the useEffect
function is being run.
If we want the effect to run in the same phase that componentDidUpdate
does, we can use useLayoutEffect
instead.
Example
const { useState, useRef, useLayoutEffect } = React;function ComponentDidUpdateFunction() { const [count, setCount] = useState(0); const firstUpdate = useRef(true); useLayoutEffect(() => { if (firstUpdate.current) { firstUpdate.current = false; return; } console.log("componentDidUpdateFunction"); }); return ( <div> <p>componentDidUpdateFunction: {count} times</p> <button onClick={() => { setCount(count + 1); }} > Click Me </button> </div> );}ReactDOM.render( <ComponentDidUpdateFunction />, document.getElementById("app"));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script><div id="app"></div>
You can turn it into custom hooks, like so:
import React, { useEffect, useRef } from 'react';const useDidMountEffect = (func, deps) => { const didMount = useRef(false); useEffect(() => { if (didMount.current) func(); else didMount.current = true; }, deps);}export default useDidMountEffect;
Usage example:
import React, { useState, useEffect } from 'react';import useDidMountEffect from '../path/to/useDidMountEffect';const MyComponent = (props) => { const [state, setState] = useState({ key: false }); useEffect(() => { // you know what is this, don't you? }, []); useDidMountEffect(() => { // react please run me if 'key' changes, but not on initial render }, [state.key]); return ( <div> ... </div> );}// ...
I made a simple useFirstRender
hook to handle cases like focussing a form input:
import { useRef, useEffect } from 'react';export function useFirstRender() { const firstRender = useRef(true); useEffect(() => { firstRender.current = false; }, []); return firstRender.current;}
It starts out as true
, then switches to false
in the useEffect
, which only runs once, and never again.
In your component, use it:
const firstRender = useFirstRender();const phoneNumberRef = useRef(null);useEffect(() => { if (firstRender || errors.phoneNumber) { phoneNumberRef.current.focus(); }}, [firstRender, errors.phoneNumber]);
For your case, you would just use if (!firstRender) { ...
.