When something changes in a component, there are things you might want to do.
- For eg, a user types text into an autocomplete search input, then you might want to fetch autocomplete suggestions by making a call to server and fetch suggestions.
- Sometimes a user goes to another page and you want to log all the pages a user went in a session to figure how best to serve the user.
- Or perhaps, you want to store all the user’s preferences in the browser’s localStorage (for eg when a user updates his preference of viewing your site from light mode to dark mode etc)
There are so many such things you might want to do in your app, in response to updates in your components. These are called “side-effects”. Let's learn how to run side-effects in React!
*Note: This article is a part of the Delightful React Series, as part of which, I am releasing one chapter every day for 25 days. Please support me in anyway you can! *
We are at this point so far. Feel free to use this codesandbox as a starting point for this chapter and add the code instructions as mentioned in the chapter.
Side-effects
Side-effects are simply things you want to perform when an update occurs in components. They are called side-effects because they generally don’t interfere with React’s process. They are generally used to interact with other aspects of the browser like localStorage, api calls, console.log etc.
React offers a built-in hook to seamlessly run side-effects.
The useEffect hook
The useEffect hook, as the name suggests, is a built-in hook to run a side-effect each time the component rerenders. When a component instance is used anywhere in an app, it renders at least once. And when it’s props or state change, it renders again.
The useEffect hook can be used to run a function each time a component renders. Let’s try it out. First, let’s import useEffect from React.
//pages/index.js
import { useState, useEffect } from "react";
Runnings Side effects
To run a side-effect, simply pass a function as argument to useEffect. If we use this inside our home page, AllPosts Component, like so, we should see the document.title be updated as we type into our search form.
//pages/index.js
useEffect(() => {
console.log("running a side effect...");
document.title = searchText;
});
- Side-effects are simply functions that can be run to interact with other parts of a web experience (besides rendering elements which React does).
- Side-effects can be run after a component renders. Basically, it's like React is saying "I just rendered after some updates, do you want to do something too, like a by-product of this render?"
- So, in this case, we are interacting with
document.title
which we can update using this useEffect use. - So useEffect simply takes a function that it will run after each render in this case. And in that function it is going to set the
title
of thedocument
to whatever the value ofsearchText
is at that point. - As searchText changes and the component rerenders, the effect runs and title gets updated.
What can you do within side effects?
A lot of things can be done with side-effects. Some of them include:
- Updating document title
- Making API calls to servers
- Handling events in the browser
We are going to cover quite a few scenarios in this book.
Understanding Render and Commit Phase
While rendering elements and components, React has a few phases but they can roughly be categorised into render and commit phase. We will talk more about this in a future chapter but this diagram should give a rough idea.
Let's learn more about useEffect
Let's update our app a little bit and we can understand how we can use useEffect better. Let's start by using another state variable to filter our posts even more.
Our blogposts also have a boolean field called featured which we can use to to show only the featured posts in our search interface.
Let's create a state variable and a checkbox input right beside our input and filter only featured posts.
//pages/index.js
const [showFeaturedOnly, setShowFeaturedOnly] = useState(false);
//pages/index.js
<div className="search-posts">
<input
type="text"
value={searchText}
onChange={function (event) {
setSearchText(event.target.value);
}}
/>
<div className="flex place-items-center">
<input id="is-featured" type="checkbox" />
<label htmlFor="is-featured">Featured</label>
</div>
</div>
- Now, let's use our state variable and make our input controlled and update it's value as and when the input is checked. This is similar to our Chapter 13, where we modified a text input with a state variable.
//pages/index.js
<input
value={showFeaturedOnly}
onChange={function (event) {
setShowFeaturedOnly(event.target.checked);
}}
id="is-featured"
type="checkbox"
/>
- Now finally, let's modify our filtering logic to filter posts further if the
showFeaturedOnly
state variable is true. If it is true, we will filter further and only select blogposts which match the text string and ifblogPostObject.featured
is true.
const filteredBlogPosts = BLOGPOSTS.filter((blogPostObject) => {
// if searchText is blank then show all posts
if (!searchText) {
return true;
}
if (blogPostObject.title.includes(searchText)) {
return true;
} else {
return false;
}
}).filter((blogPostObject) => {
if (showFeaturedOnly) {
return blogPostObject.featured;
} else {
return true;
}
})
Our filtering logic should now work perfectly.
Specifying dependencies
So now, let's talk about the useEffect
again. If we just a console log statement like this inside our useEffect, we should see that this console statement runs after every render.
//pages/index.js
useEffect(() => {
console.log("setting document title to "+searchText);
document.title = searchText;
},[searchText]);
All renders so far include
- initial render
- when the text input state variable updates
when the checkbox input state variable updates
But we don't need to update the document.title when the checkbox state variable changes do we? So we want to use a little more control with our effect.
Thankfully, React comes with such feature built in.
- useEffect comes with a second argument and this argument is the list of dependencies that this useEffect needs to track and if any of these dependencies change, only then it rerenders.
- This dependency array needs to be an array and we need to pass all of our dependencies into it. Since we only want to track
searchIndex
we need to put that into it like so.
//pages/index.js
useEffect(() => {
console.log("setting document title to "+searchText);
document.title = searchText;
},[searchText]);
Now, we can see that this useEffect
only executes one time after mount and then after each change to the searchText
variable.
- Let's try a couple more
useEffects
like so.
function AllPosts() {
const [searchText, setSearchText] = useState("");
const [showFeaturedOnly, setShowFeaturedOnly] = useState(false);
useEffect(() => {
console.log("setting document title to "+searchText);
document.title = searchText;
},[searchText]);
useEffect(() => {
console.log("This side effect runs only when showFeaturedOnly changes");
},[showFeaturedOnly]);
useEffect(() => {
console.log("Tracking changes in both showFeaturedOnly and searchText");
},[showFeaturedOnly, searchText]);
//...
}
All useEffects fire after first render
An important point to remember is that all useEffect
s run atleast once. Irrespective of the dependencies set, all useEffect
's run after the first render.
Right, that's it for this chapter.
- Play around with the sandbox below and see how they run. This is the updated sandbox for this chapter with the final codebase. Feel free to fork it and try various dependencies in it.
Great work!
We just started off with the useEffect
hook and there is so much more to learn. Stay tuned!
Thanks and Please support my work
- Writing blog posts and making videos is an effort that takes many hours in my day. I do it because I love teaching and make great content.
- However, I need your support too. Please support my work and follow my social accounts to help me continue to make great content.
- Here are my social links.
Follow me on Twiter
Subscribe to my channel on Youtube
Thank you!