How React’s useEffect Hook Changed My Life
William Bojczuk
Posted on April 14, 2023
Oh my God, why can’t my JavaScript find my React Component. WhAt Am I DOiNg WronGGg!!!!?!?
Is probably something every developer starting out with React has said at some point. Luckily the creators of React gave us a hook to combat this.
Any code that alters a component after the component has been mounted is called a “side effect”. To accomplish this, us developers are left with the useEffect hook.
In the below example, we are attempting to replace the textContent of a paragraph element with new text. But with this setup, an error will be thrown and no changes will be made. WHY?
function Content(){
document.getElementById("content").textContent = "New Text";
return(
<p id="content">Replace This Text</p>
)
}
I’ll tell you why, components in React are still JavaScript objects until they have been mounted/rendered. In this example the statement to change the textContent of the paragraph is actually executed BEFORE the React Component has been returned, much less mounted.
So what can you do to access the component AFTER it’s been rendered to the DOM as HTML?
Now we come to it, the how. If we update our example to use the useEffect hook to get the job done, then it does what it sounds like, it gets the job done!
Now keep in mind that you DO NOT put your code directly into the useEffect function’s arguments. useEffect takes a function which is executed upon triggering. The simplest way for me is to pass an anonymous function containing all my JavaScript to the hook like in the example below. But you could include a named function instead.
function Content(){
React.useEffect(()=>{
document.getElementById("content").textContent = "New Text";
});
return(
<p id="content">Replace This Text</p>
)
}
Now Guess What? The code works and will now change the textContent of our paragraph to “New Text”.
What about async data????
If we have a value that we are fetching from an API. We kind of need to update the part of our application that relies on that data once we receive it right? RIGHT.
In the below example, I’ve altered our Content component to rely on some data from an API. Using useEffect, we define and use an async function to fetch our data and set the state of our text to the response text.
function Content(){
const [contentText, setContentText] = React.useState("Replace This Text");
React.useEffect(()=>{
getData();
async function getData(){
const fetchData = await fetch("https://urltoapi.com");
const textData = await fetchData.text();
setContentText(textData);
}
});
return(
<p id="content">{contentText}</p>
)
}
React does not allow for the function passed to useEffect to be asyncronous. So you have to define it INSIDE of the passed function and execute it there.
But William, my code runs every single time the component is updated and it’s breaking my code!
Yes, yes it does. This is by design. By default, useEffect is run every time the component needs to update. For those of us that need to limit this like when you’re developing a SPA (single page application) and you need to manually trigger useEffect at a certain time, luckily, again we are saved by the React creators.
useEffect takes a second optional argument. An array of dependency states/values. Whenever one of these state’s values changes is when the useEffect hook is re-triggered. Whether this be on the component updating or any time in between.
Below I’ve altered our Content component to trigger useEffect every time the “Trigger useEffect” button is clicked, which changes the triggerCount state, and that state is a value in the useEffect’s dependency array.
function Content(){
const [triggerCount, setTriggerCount] = React.useState(1);
function handleClick(){
setTriggerCount((oldval) => ++oldval);
}
React.useEffect(()=>{
console.log(`useEffect has been triggered ${triggerCount} time(s)!`);
}, [triggerCount]);
return(
<button onClick={handleClick}>Trigger useEffect</button>
)
}
But I only want it to trigger ONCEEE!!!
Well, if you give useEffect an empty array as the dependency array, the hook will only trigger once, and this is when the component has been mounted/rendered. This is because when you provide a dependency array, useEffect waits for one of those values to change before it re-triggers. But if you don’t provide any values, they cannot change, so it will not re-trigger.
So for those who learn visually, here’s an alteration of the first example where useEffect only triggers once.
function Content(){
React.useEffect(()=>{
document.getElementById("content").textContent = "New Text";
}, []);
return(
<p id="content">Replace This Text</p>
)
}
So that’s it for this article. I really hope you learned something new and enjoyed it in the process. I did spend a bit of time writing this so if you did enjoy reading it, let me know :D.
For more tips, tricks and tutorials follow me on YouTube or Instagram.
Posted on April 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.