3

This code runs when name is changed (as it should) but it also runs when the component first loads. How can I have it run only on subsequent changes to name and not the first time the default value is set?

const [name, setName] = useState('');

useEffect(() => {
  alert('Hi ' + name);
}, [name]);
  • you can check some approaches [here](https://stackoverflow.com/questions/53179075/with-useeffect-how-can-i-skip-applying-an-effect-upon-the-initial-render) – buzatto May 21 '20 at 03:55
  • afaik, the react guide defines `The Effect Hook, useEffect, adds the ability to perform side effects from a function component. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API.` so not sure if you can prevent it from running during `componentDidMount()` so maybe you are looking for `useLayoutEffect` i think. just did a quick google https://stackoverflow.com/a/53254028/6141587 – deadcoder0904 May 21 '20 at 03:56

3 Answers3

4

Using a ref you can track the first render, set to false after the first render.

const firstRenderRef = useRef(true);
const [name, setName] = useState('');

useEffect(() => {
  if (firstRenderRef.current) {
    firstRenderRef.current = false;
  } else {
    alert('Hi ' + name);
  }
}, [name]);

I'd factor this into a custom hook though

const useSkipFirstEffect = (callback, dependencies) => {
  const firstRenderRef = useRef(true);

  useEffect(() => {
    if (firstRenderRef.current) {
      firstRenderRef.current = false;
    } else {
      callback();
    }
  }, [callback, ...dependencies]);
};

Usage:

useSkipFirstEffect(() => {
  alert('SkipFirstEffect: Hi ' + name);
}, [name]);

Edit useEffect skip first render

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • 1
    A question from someone who's learning React — is there a reason you need _useRef()_? Would a boolean not achieve the same thing? – Rich Aug 04 '21 at 20:23
  • 1
    @Rich This code ***is*** using a boolean. If you are asking why we store the boolean value in a React ref, this is because in React function components all local variables are redeclared each time the component is rendered, i.e. each time the function is called. The ref is a bucket that we can mutate at-will and the value persists through rerenders. Also, updating a React ref doesn't trigger rerenders (*such as would be case if we used local state to track the initial render*). – Drew Reese Aug 04 '21 at 20:39
1

There are some ways of skipping effect on initial render(like ref and using a variable). What I generally use and found the most simplest way is using the return of useEffect. It seems to me this is a bit hackish way but it works -

const [name, setName] = useState('');

useEffect(() => {
  //don't do anything on initial render
  return () => {
      alert('Hi ' + name);
   }
}, [name]);

Now this will run only when dependency name changes. I am not sure if it's recommended but it always worked for me without any problems. Leaving here in case it helps.

NOTE:: This will also run on component unmount change. To prevent that you can just use a check.

Atin Singh
  • 3,624
  • 2
  • 18
  • 25
0

This is causing the custom hook to re-run multiples times because you are re-creating a new array in the array of dependencies [callback, ...dependencies] I'd change it like so:

import { useEffect, useRef } from 'react'

function useEffectSkipFirst(callback, dependencies) {
  const firstRenderRef = useRef(true)

  useEffect(() => {
    if (firstRenderRef.current) {
      firstRenderRef.current = false
      return
    }
    callback()
  }, dependencies)
}

export default useEffectSkipFirst
jonidelv
  • 623
  • 1
  • 8
  • 12