Labs ICT
Pro Login

useEffect Hook

Handling side effects in functional components.

What is useEffect?

The useEffect hook lets you perform side effects in functional components. Side effects are operations that interact with the outside world — fetching data, setting up subscriptions, timers, or manually changing the DOM. Components shouldn't do these things directly in their render logic.

Think of useEffect as telling React: "After you render, do this thing." It runs after the component has been painted to the screen, so it doesn't block the UI from appearing.

Basic useEffect

Here's the simplest form of useEffect — run something once when the component mounts:

import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    // Cleanup: clear the interval when component unmounts
    return () => clearInterval(interval);
  }, []); // Empty array = run once on mount

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text style={{ fontSize: 48 }}>{seconds}s</Text>
    </View>
  );
}

export default Timer;

The empty dependency array ([]) means the effect only runs once when the component mounts. Without it, the effect would run on every render, creating a new interval each time. The cleanup function (return () => clearInterval(...)) prevents memory leaks by cleaning up when the component unmounts.

Running on Changes

You can make useEffect run when specific values change by adding them to the dependency array.

function UserPosts({ userId }) {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetchPosts(userId).then(setPosts);
  }, [userId]); // Re-run when userId changes

  return (
    <View>
      {posts.map(post => (
        <Text key={post.id}>{post.title}</Text>
      ))}
    </View>
  );
}

When userId changes, the effect re-runs and fetches the new user's posts. Without the dependency array, it would run on every render. With an empty array, it would only run once and never update when userId changes. The dependency array is what makes useEffect so flexible.

Common Patterns

Here are some practical useEffect patterns you'll use often:

// Fetch data on mount
useEffect(() => {
  fetchData().then(setData);
}, []);

// Subscribe to an event
useEffect(() => {
  const unsubscribe = EventEmitter.addListener('update', handleUpdate);
  return () => unsubscribe();
}, []);

// Respond to prop changes
useEffect(() => {
  document.title = `Count: ${count}`;
}, [count]);

// Run after every render (use sparingly!)
useEffect(() => {
  console.log('Component rendered');
});

The most common patterns are fetching data on mount, subscribing to events with cleanup, responding to specific prop changes, and (rarely) running after every render. The dependency array is key to controlling when your effect runs. Getting it right prevents bugs and performance issues.