Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /React
  4. /Cleanup Functions
Your Progress0%
0 of 42 completed

React Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App

Cleanup Functions

Preventing memory leaks and cleaning up side effects

Side effects often need to be cleaned up when they're no longer needed. Timers need to be cleared, subscriptions need to be unsubscribed, and fetch requests should be cancelled. Without cleanup, you get memory leaks - your app uses more and more memory, slows down, and can even crash. In this lesson, you'll learn when and how to clean up effects properly, preventing bugs and building professional React applications. ๐Ÿงน

What Is Cleanup?

A cleanup function is code that runs to reverse or stop an effect:

cleanup-basic.jsx
React.useEffect(() => {
  // Effect code runs after render
  console.log('Effect: Setting up...')
  
  // Cleanup function (optional)
  return () => {
    console.log('Cleanup: Tearing down...')
  }
}, [])

// When cleanup runs:
// 1. Before effect re-runs (if dependencies change)
// 2. When component unmounts (removed from screen)

When Does Cleanup Run?

  1. Before the effect re-runs - Clean up previous effect before new one
  2. When component unmounts - Clean up when component removed

Cleanup doesn't run on the first render, only on subsequent re-runs and unmount.

Why Cleanup Is Important

Problem: Memory Leak Without Cleanup

memory-leak.jsx
function Timer() {
  const [count, setCount] = React.useState(0)
  
  React.useEffect(() => {
    // โŒ BAD - Memory leak!
    setInterval(() => {
      setCount(prev => prev + 1)
    }, 1000)
  }, [])
  
  return <p>Count: {count}</p>
}

// Problem:
// 1. Component mounts, starts interval
// 2. Component unmounts (user navigates away)
// 3. Interval keeps running!
// 4. setCount tries to update unmounted component
// 5. Memory leak + React warnings

Solution: Clear Interval on Cleanup

cleanup-timer.jsx
function Timer() {
  const [count, setCount] = React.useState(0)
  
  React.useEffect(() => {
    // โœ… GOOD - Cleanup prevents memory leak
    const intervalId = setInterval(() => {
      setCount(prev => prev + 1)
    }, 1000)
    
    // Cleanup: Clear interval when component unmounts
    return () => {
      clearInterval(intervalId)
      console.log('Interval cleared')
    }
  }, [])
  
  return <p>Count: {count}</p>
}

// Flow:
// 1. Component mounts, interval starts
// 2. Component unmounts
// 3. Cleanup runs, interval cleared
// 4. No memory leak!

Common Cleanup Patterns

1. Cleaning Up Timers

cleanup-timers.jsx
// setInterval - Repeating timer
function IntervalTimer() {
  const [count, setCount] = React.useState(0)
  
  React.useEffect(() => {
    const id = setInterval(() => {
      setCount(prev => prev + 1)
    }, 1000)
    
    return () => clearInterval(id)  // Cleanup
  }, [])
  
  return <p>{count}</p>
}

// setTimeout - One-time timer
function DelayedMessage() {
  const [message, setMessage] = React.useState('')
  
  React.useEffect(() => {
    const id = setTimeout(() => {
      setMessage('Hello after 3 seconds!')
    }, 3000)
    
    return () => clearTimeout(id)  // Cleanup
  }, [])
  
  return <p>{message}</p>
}

// Why cleanup timeouts?
// If component unmounts before timeout fires,
// setMessage will try to update unmounted component

2. Cleaning Up Event Listeners

cleanup-events.jsx
function MouseTracker() {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  
  React.useEffect(() => {
    const handleMouseMove = (event) => {
      setPosition({ x: event.clientX, y: event.clientY })
    }
    
    // Add listener
    window.addEventListener('mousemove', handleMouseMove)
    
    // Cleanup: Remove listener
    return () => {
      window.removeEventListener('mousemove', handleMouseMove)
    }
  }, [])
  
  return <p>Mouse: {position.x}, {position.y}</p>
}

// Without cleanup:
// - Listener stays attached after component unmounts
// - Multiple listeners accumulate if component remounts
// - Memory leak!

3. Cleaning Up Subscriptions

cleanup-subscriptions.jsx
function ChatRoom({ roomId }) {
  const [messages, setMessages] = React.useState([])
  
  React.useEffect(() => {
    // Subscribe to chat room
    const subscription = chatAPI.subscribe(roomId, (message) => {
      setMessages(prev => [...prev, message])
    })
    
    // Cleanup: Unsubscribe
    return () => {
      subscription.unsubscribe()
      console.log('Unsubscribed from room', roomId)
    }
  }, [roomId])
  
  return (
    <div>
      {messages.map((msg, i) => <p key={i}>{msg}</p>)}
    </div>
  )
}

// When roomId changes:
// 1. Cleanup runs: unsubscribe from old room
// 2. Effect runs: subscribe to new room

4. Aborting Fetch Requests

cleanup-fetch.jsx
function UserProfile({ userId }) {
  const [user, setUser] = React.useState(null)
  const [isLoading, setIsLoading] = React.useState(true)
  
  React.useEffect(() => {
    // Create AbortController
    const controller = new AbortController()
    
    const fetchUser = async () => {
      try {
        setIsLoading(true)
        
        const response = await fetch(
          `/api/users/${userId}`,
          { signal: controller.signal }  // Pass signal
        )
        
        const data = await response.json()
        setUser(data)
      } catch (error) {
        // Ignore abort errors
        if (error.name !== 'AbortError') {
          console.error('Fetch failed:', error)
        }
      } finally {
        setIsLoading(false)
      }
    }
    
    fetchUser()
    
    // Cleanup: Abort fetch
    return () => {
      controller.abort()
      console.log('Fetch aborted for user', userId)
    }
  }, [userId])
  
  if (isLoading) return <p>Loading...</p>
  return <div>{user.name}</div>
}

// When userId changes:
// 1. Cleanup aborts old request
// 2. Effect starts new request
// Prevents race conditions and unnecessary state updates!

5. Cleaning Up External Libraries

cleanup-libraries.jsx
function Map({ center }) {
  const mapRef = React.useRef(null)
  
  React.useEffect(() => {
    // Initialize external library
    const map = new MapLibrary(mapRef.current, {
      center: center,
      zoom: 10
    })
    
    console.log('Map initialized')
    
    // Cleanup: Destroy library instance
    return () => {
      map.destroy()
      console.log('Map destroyed')
    }
  }, [center])
  
  return <div ref={mapRef} style={{ height: '400px' }} />
}

// Examples: Map libraries, chart libraries, animation libraries
// Always check documentation for cleanup methods:
// - destroy(), remove(), dispose(), cleanup(), etc.

Understanding Cleanup Timing

cleanup-timing.jsx
function Example({ count }) {
  console.log('1. Component rendering, count =', count)
  
  React.useEffect(() => {
    console.log('3. Effect running, count =', count)
    
    return () => {
      console.log('2. Cleanup running, previous count =', count)
    }
  }, [count])
  
  console.log('4. Render complete')
  
  return <p>Count: {count}</p>
}

// First render (count = 0):
// "1. Component rendering, count = 0"
// "4. Render complete"
// (browser paints)
// "3. Effect running, count = 0"

// Second render (count = 1):
// "1. Component rendering, count = 1"
// "4. Render complete"
// (browser paints)
// "2. Cleanup running, previous count = 0"  โ† Cleanup for old effect
// "3. Effect running, count = 1"             โ† New effect

// Component unmounts:
// "2. Cleanup running, previous count = 1"  โ† Final cleanup

// Key point: Cleanup runs BEFORE next effect or on unmount

Cleanup Execution Order:

  1. Component renders with new values
  2. Browser paints the screen
  3. Cleanup function runs (if effect ran before)
  4. New effect function runs

The cleanup has access to the OLD values from the previous render!

Interactive Demo

ReactPlayground.jsx
Code Editor
Preview

๐Ÿ’ก Tip: Edit the code above and click "Run" to see your changes

When Is Cleanup Needed?

โœ… You NEED Cleanup For:

  • Timers - setInterval, setTimeout
  • Event listeners - addEventListener
  • Subscriptions - WebSocket, Firebase, etc.
  • Fetch requests - AbortController
  • External libraries - Maps, charts, animations
  • Browser APIs - requestAnimationFrame, IntersectionObserver

โŒ You DON'T Need Cleanup For:

  • Regular state updates - setState is safe
  • localStorage/sessionStorage - No active connection
  • Simple calculations - No ongoing process
  • Console.log - Already completed

๐Ÿ’ก Rule of Thumb

If your effect:
โ€ข Starts something ongoing (timer, listener, subscription)
โ€ข Creates a connection that stays open
โ€ข Registers something that needs unregistering

Then you NEED cleanup!

If your effect just:
โ€ข Updates state once
โ€ข Reads/writes storage once
โ€ข Logs something

Then cleanup is NOT needed.

Common Cleanup Mistakes

1. Forgetting to Return Cleanup Function

mistake-no-return.jsx
// โŒ Wrong - Memory leak!
React.useEffect(() => {
  const id = setInterval(() => {
    console.log('Running...')
  }, 1000)
  
  clearInterval(id)  // This runs immediately, doesn't help!
}, [])

// โœ… Correct - Return cleanup function
React.useEffect(() => {
  const id = setInterval(() => {
    console.log('Running...')
  }, 1000)
  
  return () => clearInterval(id)  // Cleanup function
}, [])

2. Not Saving ID for Cleanup

mistake-no-id.jsx
// โŒ Wrong - Can't clear interval!
React.useEffect(() => {
  setInterval(() => {
    console.log('Running...')
  }, 1000)
  
  return () => clearInterval(???)  // What ID to clear?
}, [])

// โœ… Correct - Save ID
React.useEffect(() => {
  const id = setInterval(() => {
    console.log('Running...')
  }, 1000)
  
  return () => clearInterval(id)  // Clear the right interval
}, [])

3. Using Async Function as Cleanup

mistake-async-cleanup.jsx
// โŒ Wrong - Cleanup can't be async!
React.useEffect(() => {
  return async () => {
    await someAsyncCleanup()  // This won't work!
  }
}, [])

// โœ… Correct - Call async function inside cleanup
React.useEffect(() => {
  return () => {
    someAsyncCleanup()  // Fire and forget
    // Or wrap in promise if you need error handling
    someAsyncCleanup().catch(console.error)
  }
}, [])

4. Missing Dependencies Affects Cleanup

mistake-deps-cleanup.jsx
// โŒ Wrong - Cleanup uses stale roomId
function Chat({ roomId }) {
  React.useEffect(() => {
    chatAPI.subscribe(roomId)
    
    return () => {
      chatAPI.unsubscribe(roomId)  // Uses current roomId, but might be stale!
    }
  }, [])  // Missing roomId dependency!
}

// โœ… Correct - Include roomId in dependencies
function Chat({ roomId }) {
  React.useEffect(() => {
    chatAPI.subscribe(roomId)
    
    return () => {
      chatAPI.unsubscribe(roomId)  // Uses roomId from effect's closure
    }
  }, [roomId])  // When roomId changes, cleanup uses old value, effect uses new
}

Key Takeaways

  • Cleanup functions prevent memory leaks and bugs
  • Return a function from useEffect to run cleanup
  • Cleanup runs before effect re-runs and on unmount
  • Always clear timers: clearInterval, clearTimeout
  • Always remove event listeners: removeEventListener
  • Always unsubscribe from subscriptions
  • Abort fetch requests with AbortController
  • Destroy external library instances
  • Cleanup has access to old values from previous render
  • Not all effects need cleanup - only ongoing processes
  • Save IDs/references needed for cleanup
  • Cleanup functions can't be async
  • Missing cleanup causes memory leaks and React warnings

What's Next?

You now understand cleanup functions and how to prevent memory leaks! You know when cleanup is needed and how to implement it properly for different scenarios.

In the next lesson, you'll learn about Dependency Arrays:

  • Understanding exactly when effects run
  • Common dependency array mistakes
  • Infinite loop prevention
  • When to use empty array vs no array
  • Optimizing effect execution

Get ready to master dependency arrays and control your effects precisely! ๐ŸŽฏ

Learning how to prevent memory leaks with cleanup functions in React!

Previous
Data Fetching
Next
Dependency Arrays

Never Miss a New React Tutorial

Join 2,000+ developers learning React step-by-step. Get new tutorials, tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

React Tutorials

0 of 42 completed

Your Progress0%

Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

ยฉ 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo