A quick explanation on useEffect
bdbch

bdbch @bdbchgg

About: Frontend Engineer @ Tiptap Building a modular and framework agnostic editor library for everyone. Likes cats and video games.

Location:
Berlin
Joined:
Jan 9, 2019

A quick explanation on useEffect

Publish Date: Jul 20 '19
42 7

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!

Comments 7 total

  • Manoj Kumar Rajasekar
    Manoj Kumar RajasekarJul 21, 2019

    Thanks for the Clean and Simple explaination !

  • Emma Goto 🍙
    Emma Goto 🍙Jul 21, 2019

    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?

    • bdbch
      bdbchJul 21, 2019

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

  • Luminessa Starlight
    Luminessa StarlightJul 24, 2019

    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.

    • bdbch
      bdbchJul 24, 2019

      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?

      • Luminessa Starlight
        Luminessa StarlightJul 24, 2019

        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.

        • bdbch
          bdbchJul 25, 2019

          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.

Add comment