loading...
Cover image for A quick explanation on useEffect

A quick explanation on useEffect

bdbch profile image Dominik Biedebach Updated on ・2 min read

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!')
}, [])

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>
}

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])

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!

Posted on by:

bdbch profile

Dominik Biedebach

@bdbch

I produce code. - Prefers React over Vue - Prefers VSCode over any other editor - Likes React Native - Absolutely loves GraphQL / Apollo

Discussion

pic
Editor guide
 

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.

 

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?

 

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.

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.

 

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?

 

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

 

Thanks for the Clean and Simple explaination !