First of all, I want to state the obvious: React Class Components are dead 💀
This post is about how the react render lifecycle works on functional components.
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
For specific side effects linked to one or more states, you can add them to the dependencies array.
useEffect
will then execute whenever any of those states change:
useEffect(() => {
alert('I run whenever counterValue changes.')
}, [counterValue])
Remember, it's a good practice to include only the states used in the dependencies array.
Be mindful of the "no dependencies" scenario, as it could lead to unnecessary effect runs and performance issues.
Stay tuned for more on React rendering, where we'll delve into useCallback
and useMemo
hooks in future posts!