7

Why the log still trigger although I didn't do setTest to change the test value? I expect for the first time the log should not trigger because the test value remain 0?

function App() {
  const [test, setTest ] = useState(0)

  useEffect(() => {
    console.log('log')
  }, [test])

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

I have a scenario like this. on first load don't do anything, then if user select an option on the page then do something, how do I do that with useEffect?

Rachel V
  • 107
  • 1
  • 2
  • 6
  • It will always run the first time. – Domino987 Aug 06 '19 at 17:14
  • @Domino987 do I have to compare prev and current value? how do I do that with hook? – Rachel V Aug 06 '19 at 17:21
  • UseEffect compares prev and current values (the ones you pass as a second parameters) for you. On the frist render, the prev value is undefined so that it differs from the current value. and that is why it gets executed. – Domino987 Aug 06 '19 at 17:24
  • @Domino987 the problem is not why it gets executed, I want to get rid of the warning. – Rachel V Aug 07 '19 at 16:51
  • Possible duplicate of [With useEffect, how can I skip applying an effect upon the initial render?](https://stackoverflow.com/questions/53179075/with-useeffect-how-can-i-skip-applying-an-effect-upon-the-initial-render) – Domino987 Aug 07 '19 at 17:07

2 Answers2

4

(1)first useEffect call is when component mount.

useEffect(() => {
    console.log('log')
  }, [test])

(2)if you pass second Array dependencies argument to useEffect and that argument wasn't empty, any time one of that dependency changes useEffect also recalled.

useEffect(() => {
    console.log('log')
  }, [test])

(3) if array of dependencies was empty, useEffect only one time called(after component mount for first time).

useEffect(() => {
    console.log('log')
  }, [])

(4)if useEffect implementing without second argument any time component state update useEffect also called.

useEffect(() => {
    console.log('log')
  })

for your situation you can check if test is 0 cancel execute useEffect body code like this:

function App() {
  const [test, setTest ] = useState(0)

  useEffect(() => {
   if(test < 1) return;
    console.log('log')
  }, [test])


  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}
Hamid Pouladi
  • 215
  • 2
  • 4
  • 1
    @RachelV, don't be rude. He is only trying to help and and his last code segment is exactly what you need. Also your question is a duplicate, so next time, please check if there is already an answer to that question. – Domino987 Aug 07 '19 at 17:11
  • 1
    Hamid for president. – fruitloaf Jan 12 '22 at 10:33
  • This is the simplest, most straightforward explanation I've seen for useEffect's different invocation styles. I wish it was possible to save stackoverflow answers! – Caleb Jay Apr 26 '22 at 07:20
3

From https: Using the Effect Hook

What does useEffect do? By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates.

Your component rendered, useEffect triggered, and your log ran.

It sounds like you might be confusing useEffect (which only takes a single argument, a callback) with other hooks that take multiple arguments.

---- EDIT (Comments-Related) ----

useEffect is not a direct replacement for class-based componentDidMount and componentDidUpdate methods, but because of the timing of when it runs it can often be used to replace those methods. See for further info. You may also want to read about the related useLayoutEffect hook.

If your intent is to hook up some logic "on load", such as an event handler, you don't need a hook to do that. Instead you just need a React event handler attribute, eg.

<option onChange={changeHandler} />

(See for further info.)

FaFryk
  • 59
  • 1
  • 9
machineghost
  • 33,529
  • 30
  • 159
  • 234
  • I thought useEffect is like componentDidUpdate? says I want to fire a http request when test changed, how do I do that using useEffect? – Rachel V Aug 06 '19 at 17:14
  • They're not exactly the same, but `useEffect` can be used in many of the same cases. From https://reactjs.org/docs/hooks-reference.html#useeffect: "Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint, during a deferred event. This makes it suitable for the many common side effects, like setting up subscriptions and event handlers, because most types of work shouldn’t block the browser from updating the screen." – machineghost Aug 06 '19 at 17:16
  • Also see `useLayoutEffect`. From that same URL: "**Tip** If you’re migrating code from a class component, note useLayoutEffect fires in the same phase as componentDidMount and componentDidUpdate. However, **we recommend starting with useEffect first** and only trying useLayoutEffect if that causes a problem." – machineghost Aug 06 '19 at 17:17
  • I have a scenario like this. on first load don't do anything, then if user select an option on the page then do something, how do I do that with useEffect? – Rachel V Aug 06 '19 at 17:20
  • 1
    In short, you don't: `useEffect` is the wrong tool for that job. Instead you want to use a `change` (or `blur`, or some similar) event handler on your option(s) to trigger that logic. See: https://reactjs.org/docs/handling-events.html – machineghost Aug 06 '19 at 17:23