49

I'm new to Hooks and have been encountering some cases that have been sending me chasing my tail.

Hope someone can explain or provide solutions that make sense to me:

  1. Loading a method only once on mount of a component is confusing. I tried this method, it works, but don't understand it. Can someone explain this to me:

    const useMountEffect = fun => useEffect(fun, [])
    
    useMountEffect(() => {
      if (!isEdit) handleAddRow()
    })
    
    const handleAddRow = () => {
      let appendArr = [...listArr, '']
      setListArr(appendArr)
    }
    

Following this thread: How to call loading function with React useEffect only once

I tried just using useEffect without dependency and eslint does not like that and they recommend adding a skip next line which seems kind of hacky:

// eslint-disable-next-line
David Pell
  • 791
  • 1
  • 8
  • 16

3 Answers3

149

If I'm correct you want something similar to the componentDidMount life-cycle method. The way to do that is:

function MyComponent(props){
    useEffect(()=>{
        // do stuff here...
    }, []) // <-- empty dependency array
    return <div></div>
}

To understand what's happening you'll need to understand how the useEffect hook works. Using the useEffect hook without any dependencies will trigger the effect every time some state or prop changes and causes a re-render; however, if we pass an empty array as a dependency it will mark the effect as not dependent on anything else, so it will only trigger when the component mounts.

Sophie L
  • 3
  • 2
Prithwee Das
  • 4,628
  • 2
  • 16
  • 28
  • 34
    Why do so many people ask questions then not bother to return and reward the correct answer with a click on the answer as 'answered'? Seems very not nice to ask someone else's time w/o rewarding/thanking them. I see this all too often. How hard is it to return to a question and mark it? – Andrew H Jul 07 '20 at 18:18
  • this doesn't allow me to run an outside function once, cause I have to put the function inside the dependency array ... what do I do in those cases? – Kyle Calica-St Feb 02 '21 at 23:07
  • @KyleCalica-St If your function is defined outside of your components you don't need to pass it to the dependency array. – Prithwee Das Feb 03 '21 at 03:11
  • 3
    Oh boy... new to React here, and it seems like quite the mess moving away from class based components, and seeing this as a solution ticks all the mess boxes. "useEffect" for a one time execution behaviour like a constructor. Wonderful. – Kevin Parker Jan 26 '23 at 17:14
  • 2
    Strict mode makes useEffect to run twice. – Jonathan Orrego Feb 23 '23 at 17:54
36

The empty array [] argument tells useEffect() to run only once

It will run only once on mount, like componentDidMount used to

Explanation:

useEffect() takes 2 arguments - your function and an array of dependencies

useEffect(
    yourFunction, // <- function that will run on every dependency update
    [] // <-- empty dependency array
) 

The dependency array argument tells it when to run your function. If the dependencies don't change it won't run again. If dependencies do update it will run your function again.

If you pass in an empty array (no dependencies) then the function will only run on mount.

ESLint missing dependency error

React Hook useEffect has a missing dependency: 'xxx'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

The reason for the ESLint rule is that useEffect() is designed to run on every dependency update it will expect you to add all the dependencies to that array.

To ignore this you can add above the dependency array line:

// eslint-disable-next-line react-hooks/exhaustive-deps

Note: You almost never want to ignore the error


Note that on React 18 Strict Mode + Dev mode useEffect() will be called twice - read more about it here

Jonathan Irwin
  • 5,009
  • 2
  • 29
  • 48
  • 2
    Thanks both for your response. 1. if you leave the useEffect() without a dependency you receive an eslint error. 2. When I use `const useMountEffect = fun => useEffect(fun, [])` method I don't receive an eslint error. Can someone explain why? – David Pell Sep 25 '19 at 18:21
  • 1
    @DavidPell the dependencies argument is not required (we can see that in the source here - https://github.com/facebook/react/blob/master/packages/react/src/ReactHooks.js#L96). The ESLint rule is just there to help you since you probably do want your function to only be run when it's inputs change. Dan Abv explains more in depth here https://overreacted.io/a-complete-guide-to-useeffect/ – Jonathan Irwin Sep 26 '19 at 06:47
  • Thanks for the articles, Jonathan! – David Pell Sep 26 '19 at 13:56
  • 1
    @DavidPell did this answer your question? If so can you mark it as accepted? – Jonathan Irwin Dec 11 '20 at 13:01
  • 1
    See Prithwee Das's answer: omitting dependencies causes useEffect to be triggered on every render, but empty dependencies triggers only the first time. – Suncat2000 Jun 16 '22 at 19:58
  • 1
    Be careful, Strict Mode will fire your useEffect twice (dev mode) – Jonathan Orrego Feb 23 '23 at 17:55
  • thanks @JonathanOrrego I will add a comment – Jonathan Irwin Feb 27 '23 at 07:29
  • 1
    Disabling a linter only to resolve this error is really wrong way to do this. You should almost never omit dependency array. And logic inside your useEffect should just decide on its own if it should change state, or gracefully do nothing. – Marakoss Mar 01 '23 at 09:28
  • @Marakoss I will add a comment – Jonathan Irwin Mar 01 '23 at 09:38
6

For anyone looking to execute a function once per whole App (globally), this could be a solution:

if (typeof window !== 'undefined') { // Check if we're running in the browser.
   // ✅ Only runs once per app load
  checkAuthToken();
  loadDataFromLocalStorage();
}

function App() {
  // ...
}

Source / extended explanation here.

Mr Patience
  • 1,564
  • 16
  • 30