Reputation: 647
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
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
:
useEffect
can run the clean-up function, if clean-up function is provided.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
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