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
.
Used to call some logic specifically after a dependency is updated.
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]);
Without dependencies, the useEffect
hook runs on mount and on every re-render.
With an array of dependencies, the useEffect
hook runs on mount and runs on subsequent renders when one or all of its dependencies change.
With an empty array of dependencies, the useEffect
hook is called once on mount.
Without dependencies, the useEffect
hook runs on mount and on every re-render
With an array of dependencies, the useEffect
hook runs on mount and runs on subsequent renders when any of its dependencies change.
With an empty array of dependencies, the useEffect
Hook is called once on mount.
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
The cleanup function prevents memory leaks — a situation where your application tries to update a state memory location that no longer exists — and removes unnecessary and unwanted behaviors.
Frees application from unwanted behaviors by cleaning up effects; tidying up our code before our component unmounts.
useEffect(() => { effect(); return () => { cleanup(); }; }, [dependencies]);
Cleanup function runs before unmounting
and 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); // Cleans up the event listener when the component unmounts OR each time when the effect runs (including re-runs) 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 the return removes the event listener when the component unmounts OR when the effect runs again.
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.