React Render Lifecycle and useEffect Hook

Let me tell you something you already know: React Class Components are history 💀

In this post, we'll explore how functional components handle the react render lifecycle.

Functional Components

Components are functions that must return a JSX.Element, which means HTML tags with some JS sugar.

You can declare and export a React functional component by declaring and exporting a function.

Assuming you're familiar with useState, let's look at an example:

import React, { useState } from "react"

export const Counter = () => {
  const [counterValue, setCounterValue] = useState(0)
  const increaseValue = () => {
    setCounterValue(counterValue + 1)
  }

  return (
    <button id="value-button" onClick={increaseValue}>
      { counterValue }
    </button>
  )
}

You can declare a functional component using the function keyword as well:

export function Counter() {
  const [counterValue, setCounterValue] = useState(0)
  const increaseValue = () => {
    setCounterValue(counterValue + 1)
  }

  return (
    <button id="value-button" onClick={increaseValue}>
      { counterValue }
    </button>
  )
}

The Render process

Whenever a state or prop of the component changes, the function's code will run again.

This means that each click on the 'value-button' will:

  • Increase counterValue.
  • Redeclare increaseValue, creating a new memory reference.
  • Execute the return block.

The React useEffect Hook

Suppose you want to perform an action, like showing an alert, every time a component is rendered.

You can use the useEffect hook to add side effects to your component. It has 3 different configurations: [someStateValue], [], and no dependencies.

Here's a simple example that triggers an alert whenever the component renders:

import React, { useEffect, useState } from "react"

export const Counter = () => {
  const [counterValue, setCounterValue] = useState(0)
  const increaseValue = () => {
    setCounterValue(counterValue + 1)
  }

  useEffect(() => {
    alert('I run whenever the component renders')
  }) 

  return (
    <button id="value-button" onClick={increaseValue}>
      { counterValue }
    </button>
  )
}

In JavaScript's single-threaded environment, the Event Loop handles tasks like useEffect. The useEffect task is added to a task queue.

After the browser finishes painting the screen updates (the render), the Event Loop runs the queued tasks.

This doesn't block the rendering process.

Empty Array []

If an alert on EVERY state change sounds annoying, you can add a second parameter to useEffect, an empty array [].

This ensures that the effect runs only once after the component's initial render:

useEffect(() => {
  alert('I run once, after the component is first rendered')
}, [])

Specific Dependencies

If you want to run some side effects that depend on one or more states, you can list them in the dependencies array.

useEffect will then run every time any of those states change:

useEffect(() => {

alert('I run whenever counterValue changes.')

}, [counterValue])

It's a good practice to only include the states that are used in the dependencies array.

Be careful of the "no dependencies" scenario, as it could cause unnecessary effect runs and affect performance.

Keep an eye out for more posts on React rendering, where we'll cover useCallback and useMemo hooks in the future!

No comments:

Post a Comment