0

In my React component, I want to trigger some action only when some state is changed. I initialized the state to be a default value and put it inside the dependency array of the hook. I am not changing this state at all during the session. However, React is still running the useEffect hook after the component is mounted for the first time.

I am wondering why it's behaving like this. Is there a graceful way (or what would be the best practice) to achieve this? Thanks for the help!

The code is like this:

import React from 'react';

export function App(props) {
  const [testString, setTestString] = React.useState('abc');
  console.log(testString);
  React.useEffect(() => {
    console.log('******* I am not supposed to log here !!!')
  }, [testString]);

  return (
    <div className='App'>
      <h1>Hello React.</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

Also available at playcode.io: https://playcode.io/1458690

flyingbee
  • 601
  • 1
  • 7
  • 17

3 Answers3

1

According to the documentation for useEffect:

When your component is first added to the DOM, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values.

You could use a ref to store whether or not the component has mounted to distinguish between the initial render and an update.

See also: Equivalent to componentDidUpdate using React hooks.

Unmitigated
  • 76,500
  • 11
  • 62
  • 80
0

Listen for when the component mounts (useEffect with empty dependency array) before the other useEffect (they are run in order) and set a flag there... How graceful it is, I don't know, but it works:

import React from 'react';

export default function App(props) {
  const [testString, setTestString] = React.useState('abc');
  console.log(testString);

  let justMounted;
  React.useEffect(() => { justMounted = true }, []);

  React.useEffect(() => {
    if (justMounted) { return; }
    console.log('******* I am not supposed to log here !!!')
  }, [testString]);

  return (
    <div className='App'>
      <h1>Hello React.</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}
Thomas Frank
  • 1,404
  • 4
  • 10
0

The default behavior of the useEffect hook is that it will always be called the first time the component mounts. This is the case whether you have included a dependency array or not. This is why you are getting the behavior described in your question.

If you do include a dependency array, the useEffect will subsequently be called anytime one of the dependencies in that array changes. If you do not include a dependency array, the useEffect will only be called once, when the component first mounts.

You can use the useRef hook to create a variable, such as const firstRender = useRef() that tracks whether or not it is the first render. Inside the useEffect check if firstRender.current is true, and if so set it to false and return out of the useEffect without running any other code. If firstRender.current is false then execute the desired action you want to perform inside the useEffect.

const firstRender = useRef(true);
React.useEffect(() => {
  if (firstRender.current) {
    firstRender.current = false;
    return;
  }
  console.log('******* I am not supposed to log here !!!')
}, [testString]);`