useEffect(callback, dependency)
Performs side effects
on your components.
Side effect
is code that changes things outside the component
.cookies
or modifying HTML
.Also used to manage lifecycle of a component.
useEffect
combines the functionality of componentDidMount
, componentDidUpdate
, componentWillUnmount
of Class React components.
useEffect
is used to run some sort of logic after the render
.
You can control when
these functions will be called and can get a guarantee that some functions will run after the rendering is complete (meaning, the DOM will be accessible).
useEffect(() => { //Runs on every render }); useEffect(() => { //Runs only on the first render }, []); useEffect(() => { //Runs on the first render //And any time any dependency value changes }, [prop, state]);
If the variable referenced in the dependency array is not primitive, you will likely run into infinite render loop:
useEffect
internally compares the dependencies by reference
.const [count, setCount] = useState(0); useEffect(() => { setCalculation(() => count * 2); }, [count]); // count is a primitive number variable const [someObj, setSomeObj] = useState({}); useEffect(() => { // new object passed every time with new address setSomeObj({field1: "this", field2: "that"}); }, [someObj])
Solution to infinite rendering problem:
const [someObj, setSomeObj] = useState({}); const { field1, field2 } = someObj; useEffect(() => { setSomeObj({field1: "this", field2: "that"}); }, [field1, field2]); // manually passing each field which is primitive
Frees application from unwanted behaviors by cleaning up effects; tidying up our code before our component unmounts.
useEffect(() => { effect; return () => { cleanup; }; }, [dependencies]);
Cleanup function does not only run when our component wants to unmount — it also runs right before the execution of the next scheduled effect.
import React, { useState, useEffect } from 'react'; function MouseTracker() { const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); useEffect(() => { // Define the event handler const handleMouseMove = (event) => { setMousePosition({ x: event.clientX, y: event.clientY }); }; // Add the event listener when the component mounts window.addEventListener('mousemove', handleMouseMove); // Cleanup the event listener when the component unmounts or the effect reruns return () => { window.removeEventListener('mousemove', handleMouseMove); }; }, []); // Empty dependency array ensures this runs only once, when the component mounts return ( <div> <h1>Mouse Position</h1> <p>X: {mousePosition.x}</p> <p>Y: {mousePosition.y}</p> </div> ); } export default MouseTracker;
The cleanup function inside return removes the event listener when the component unmounts or the effect runs again (in this case, it only runs once because of the empty dependency array []).
This prevents memory leaks or unnecessary event listeners after the component is no longer needed.
Memory leak is when a memory is not released after no longer being needed by the application.
This causes the application to use more and more memory over time which leads to performance degradation and even crash.
import React, { useState, useEffect } from 'react'; function Counter() { const [count, setCount] = useState(0); useEffect(() => { const intervalId = setInterval(() => { setCount((prevCount) => prevCount + 1); }, 1000); // No cleanup function here, so the interval is never cleared }, []); // Empty dependency array, so effect runs once on mount return <div>{count}</div>; } export default Counter;
In above example, the interval counter will continue to run in the background and more resources will be consumed over time.