DEV Community

Cover image for A quick explanation on useEffect
bdbch
bdbch

Posted on • Edited on

A quick explanation on useEffect

I got asked if I could write a quick explanation on the useEffect hook provided by React and thought "Sure, that should help a few people!".

useEffect can behave like componentDidMount shouldComponentUpdate and componentWillUnmount in one function if you set it up correctly. In this post I'll show you a few ways to replicate different lifecycle behaviours.

Keep in mind that useEffect uses the second argument dependencies as a performance tool

Here is an interesting read about how you can write your hooks in general even without dependencies:

https://dev.to/samsch_org/effects-are-not-lifecycles-551o

Example as componentDidMount

First you can write an Effect that will just run once when the component mounted and will never run again:

useEffect(() => {
  console.log('I was mounted and will not run again!')
}, [])
Enter fullscreen mode Exit fullscreen mode

Important here is the empty array as a second argument. The second argument of useEffect can be used to watch properties for changes. See the following.

Example as shouldComponentUpdate

useEffect can also help with watchers on your properties so you can run it everytime a specific value is updated. Let's say we have a prop called "name" and our component should update something via effect everytime the name prop changes you could do it like this:

const MyComponent = (props) => {
  useEffect(() => {
    document.title = `Page of ${props.name}`
  }, [props.name])

  return <div>My name is {props.name} </div>
}
Enter fullscreen mode Exit fullscreen mode

You can see that we passed props.name into the array in the second argument. This will now cause the effect to always run again when the name changes.

Side note: You should always set the second argument because otherwise you can run into render loops.

Example as componentWillUnmount

useEffect can also be used to run code when the component dismounts. This is effective for subscriptions or other listeners (Websockets for example).

let bookSubscription = null
useEffect(() => {
  // stop the subscription if it already exists
  if (bookSubscription && bookSubscription.unsubscribe) bookSubscription.unsubscribe()

  // start a new subscription
  bookSubscription = startBookSubscription({ bookId: props.bookId })

  return () => {
    // stop the subscription when the component unmounts
    bookSubscription.unsubscribe()
  }
}, [props.bookId])
Enter fullscreen mode Exit fullscreen mode

You can see that now we used all options available. This code will now

  1. Start a new subscription when the component was mounted
  2. Update the subscription with the new bookId when the bookId prop changes
  3. unsubscribe the subscription when the component gets unmounted.

You can run logic whenever the component unmounts by returning a function in your effect.


I hope this quick post was helpful to you and helps you with further development. If you have questions, let me know!

Top comments (7)

Collapse
 
samsch_org profile image
Samuel Scheiderich

Sorry to be "that guy", but I think you've got it pretty wrong.

Every useEffect you write should work correctly without using the dependencies argument. If something should only happen once, you should just do it, then set some state which tells you not to do it again.

useEffect is not lifecycles.

Collapse
 
bdbchgg profile image
bdbch

Hey, thanks for your comment.

While you're technically right - they are not lifecycles - they can be used to replicate lifecycles introduced by ClassComponents.

Also the dependencies argument should always be set, because a effect without dependencies will run on every render cycle.

Can you specify why my useEffect should also work without dependencies?

Collapse
 
samsch_org profile image
Samuel Scheiderich

I just wrote an article the topic: dev.to/samsch_org/effects-are-not-...

The purpose of the second argument is to be an optimization feature, not for control flow. This isn't state particularly explicitly in the React docs, but note that it's referred to as an optimization tool here reactjs.org/docs/hooks-effect.html... and how the examples throughout the doc page don't use the deps arg.

Thread Thread
 
bdbchgg profile image
bdbch

Hey Samuel, thanks that was an interesting read! I'll link to it in my article if thats fine.

I totally agree with you that dependency arguments are meant to be used as a performance tool, not lifecycles. In my post I just used lifecycles as an example on how the different parts of an effect work and how users can replicate lifecycles using them (and the return function).

Give me a few minutes to bring in your article into my post.

Collapse
 
emma profile image
Emma Goto 🍙 • Edited

Super easy to understand!

For the last example - it looks like startBookSubscription is being called each time the prop is changed (if I'm understanding correctly), maybe it should be renamed?

Collapse
 
bdbchgg profile image
bdbch

Thats right. I'll update the code with a better example! One second.

Collapse
 
manojkrajasekar profile image
Manoj Kumar Rajasekar

Thanks for the Clean and Simple explaination !