Sean
Sean

Reputation: 647

What is the point of useEffect() if you don't specify a dependancy

I am using react hooks in a functional component, and was wondering what the use of useEffect is when you don't specify a dependency. I know the docs state it is used for side effects, but my question is why can't these side effects just be run with plain JS inside the functional component? As a very basic example I was playing with the following code:

import {useEffect, useState} from 'react'

function Child () {

  const [clickCount, updateCount] = useState(0)

  console.log('Run outside of useEffect')

  useEffect(()=>{
    console.log("Run inside of useEffect")
  })


  return (

    <button onClick = {() => updateCount(clickCount+1)}> Child COmponent </button>
  )
}

export default Child

As you would expect given it is basically just a normal JS function, on every re-render caused by a button click, both console.log's are executed.

I understand why you may want to use useEffect in a situation such as below, where you only want to run useEffect if something specific changes:

import {useEffect, useState} from 'react'

function Child () {

  const [clickCount, updateCount] = useState(0)

  console.log('Run outside of useEffect')

  //this now only runs when `someVariable` changes
  useEffect(()=>{
    
    console.log("Run inside of useEffect")
  }, [someVariable])


  return (

    <button onClick = {() => updateCount(clickCount+1)}> Child COmponent </button>
  )
}

export default Child

But this begs the question, what is the point of using useEffect unless you specify the dependency matrix as a second argument? Can't side effects just be run normally using plain JS?

Upvotes: 4

Views: 1547

Answers (2)

Yousaf
Yousaf

Reputation: 29282

what is the point of using useEffect unless you specify the dependency matrix as a second argument?

Specifying the dependency array is an optimization to tell React to skip executing the effect if certain values haven't changed between different renders of a component.

Can't side effects just be run normally using plain JS?

Effects that are executed at the top-level inside functional component execute in a different way as compared to the effect inside the useEffect hook.

When effects are inside the useEffect:

  • They are executed after browser has painted the screen, i.e. after React has applied changes to the DOM.
  • Before running the effect again, useEffect can run the clean-up function, if clean-up function is provided.
  • Specifying the dependency array will allow you to skip running the effect after every re-render of the component.
  • Omitting the array may cause an infinite re-render.

Because of the above mentioned points, you always want the side-effects to be executed inside the useEffect hook.

As far as the effects at the top-level code in the functional component are concerned, consider the following points:

  • Will execute the effect before user sees anything on the screen, i.e. effect will execute before React updates the DOM.

  • You will have no clean-up mechanism that could be executed before running the effect again.

Upvotes: 1

marzelin
marzelin

Reputation: 11600

From the docs:

effects scheduled with useEffect don’t block the browser from updating the screen. This makes your app feel more responsive. The majority of effects don’t need to happen synchronously.

Code inside useEffect hook is run after the DOM update. That's the difference.

Example:

function App() {

  let [hi, setHi] = React.useState(false);
  let [inEffect, setInEffect] = React.useState(true);
  let msg = inEffect ? "useEffect (DOM will be updated immediately)" : "functional component (it will block DOM update)";
  
  return (
    <React.Fragment>
      <p>
        Long-running code is in {msg} {" "}
        <button onClick={() => (setInEffect(!inEffect), setHi(false))}>switch</button>
      </p>
      <button onClick={() => setHi(true)}>Say Hi</button>
      { hi ? <Hi {...{inEffect}}/> : null }
    </React.Fragment>
  );
}

function Hi({inEffect}) {
  !inEffect && block();
  React.useEffect(() => inEffect && block());
  return <h1>Hi!</h1>
}

function block(time = 2000) {
  const now = Date.now();
  while(now + time > Date.now());
}


ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 5

Related Questions