Whenever a modification occurs in a prop or the state, the relevant component and its children re-render.
But what if we want to perform an action that is not part of the return? What happens after React has updated the DOM? For example, if we want to trigger an alert each time the cart is updated? Or even save it after each update?
These types of actions are called side effects, and for this, we have useEffect
, which lets us perform an action at any moment in the lifecycle of our components. 😉
Discover useEffect
Let’s give it a go. Say you want to create an alert when you add a plant to the cart and for that alert to display the cart total.
All that’s required is a little line of code in Cart.js
:
alert(`I have ${total}€ to pay 💸`)
We’ll put that directly into our component, before the return
. Go on – try it!
When I click, it blocks my code and the total only displays once I’ve clicked OK! 😱 What’s going on?
Don't worry, we won’t leave it like that.
This is where useEffect
comes into play.
Import it like we did with useState
in Cart.js
.
import { useState, useEffect } from 'react'
Use this snippet instead (still in Cart.js
):
useEffect(() => {
alert(`I have ${total}€ to pay 💸`)
})
Which gives Cart.js
:
function Cart({ cart, updateCart }) {
const [isOpen, setIsOpen] = useState(true)
const total = cart.reduce(
(acc, plantType) => acc + plantType.amount * plantType.price,
0
)
return isOpen ? (
className='jh-cart'
className='jh-cart-toggle-button'
onClick={() => setIsOpen(false)}
Close
{cart.length > 0 ? (
Cart
{cart.map(({ name, price, amount }, index) => (
key={`${name}-${index}`}
{name} {price}€ x {amount}
))}
Total:{total}€
onClick={() => updateCart([])}Open Cart
) : (
Your cart is empty
)}
) : (
className='jh-cart-closed'
className='jh-cart-toggle-button'
onClick={() => setIsOpen(true)}
Open Cart
)
}
It’s working, and for the simple reason that useEffect
lets you perform your effect once the component has finished rendering. And because useEffect is directly within the component, you have direct access to your state, variables, and props. Fantastic, isn’t it?
But wait, what happens if I close my cart?
Nooooo! 😫 My alert triggers again!
Of course – useEffect
triggers every time the component renders.
Specify When to Trigger an Effect With Dependencies
To decide exactly when you want an effect to be triggered, you can use a list of dependencies. This is the second parameter passed to useEffect
.
That function is the effect to be run. In this case:
() => {
alert(`I have ${total}€ to pay 💸`)
}
The second parameter of useEffect
accepts a list written between square brackets: this is your list of dependencies.
If you want to only display an alert when your cart total changes, all you have to do is:
useEffect(() => {
alert(`I have ${total}€ to pay 💸`)
}, [total])
You can put whatever variable you want here. If you want to display an alert when the total changes or when a new category is selected, it’s no problem to:
Get the selected category (by lifting up
activeCategory
andsetActiveCategory
and passing them as props).Put
[total, activeCategory]
in your list of dependencies.
Give it a go and see for yourself!
The alert will be displayed either when the category or the total changes.
Will this effect be triggered after the first time my component renders?
Try refreshing the page to see! The alert displays, so the answer is yes.
Although this question leads to another one:
How do I run an effect only after the first time my component renders? For example, if I want to get data on an API?
For this, you need to provide an empty list of dependencies:
useEffect(() => {
alert('Welcome to Jungle House')
}, [])
Change the Title of Your Tab
Are you getting fed up with all these alerts? Wouldn't you rather use useEffect
to update the title of your browser tab?
To do this, use document.title
, still in Cart.js
:
useEffect(() => {
document.title = `JH: ${total}€ Purchases`
}, [total])
Ta-da! The title of the tab changes according to the cart total! 🥳.
Play by the Rules With useEffect
Let’s go back over what you’ve just learned about useEffect as well as its other uses in this video!
As explained in the last chapter, useEffect
is a hook, a function that allows you to hook into the functionality of React effects. However, there are a few special rules that apply.
Always call
useEffect
in the root of your component. You cannot call it inside a loop, conditional code, or nested functions. This way, you’ll avoid unwanted errors.Like for
useState
,useEffect
is only accessible within a React function component. It’s not possible to use it within a class component or in a simple JavaScript function.
A good practice is to separate the different actions being performed in different useEffects.
Give It a Go!
It’s now time to put what you’ve learned into practice. You’ll find the codebase on branch P3C3-Begin.
Here, you’re going to let users save their cart, even when the page is refreshed using localStorage . To ensure the best possible user experience, you should do the following:
Save the cart after each modification with
localStorage.setItem()
. Careful – as your cart is now an object, you’ll have to useJSON.stringify
(documentation can be found here) thenJSON.parse
(documentation here).Ensure the cart is not saved more than required.
Get the cart when the page is first loaded with
localStorage.getItem
.
Once you’re done, compare your code on branch P3C3-Solution.
Let’s Recap!
Use
useEffect
to perform effects: it lets your component run actions after rendering, choosing when this action should be run.The hook
useEffect
is called after each time your component renders. It is possible to specify which data modification triggers the effects run in useEffect with a list of dependencies.An empty list of dependencies lets you run an effect only the first time your component is rendered.
In the next part, we’ll have a quick recap of everything you’ve learned, and I’ll give you a few ideas of ways to continue your learning. See you there!