đŹ React Native Effects: The Movie Director Inside Your App
Imagine youâre a movie director. You donât just shoot scenesâyou also handle what happens before a scene (setting up cameras), during the scene (action!), and after (cleaning up props). Thatâs exactly what Effects do in React Native!
Effects are your appâs behind-the-scenes crew. They handle all the work that happens outside of drawing the screenâlike fetching data, setting up timers, or listening for events.
đ The useEffect Hook: Your Action Crew
What is useEffect?
Think of useEffect as telling your crew: âAfter you paint the scene on screen, go do this task.â
It runs after the component appears (renders). Perfect for:
- Fetching data from the internet
- Setting up timers
- Subscribing to events
Simple Example
import { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
function WelcomeScreen() {
const [message, setMessage] = useState('Loading...');
useEffect(() => {
// This runs AFTER the screen appears
setMessage('Welcome to the app!');
});
return (
<View>
<Text>{message}</Text>
</View>
);
}
What happens:
- Screen shows âLoadingâŠâ
useEffectruns after screen appears- Message changes to âWelcome to the app!â
Real Life Example: Fetching User Data
function ProfileScreen() {
const [user, setUser] = useState(null);
useEffect(() => {
// Fetch user after screen loads
fetch('https://api.example.com/user')
.then(res => res.json())
.then(data => setUser(data));
});
return (
<View>
<Text>{user ? user.name : 'Loading...'}</Text>
</View>
);
}
đŻ useEffect Dependencies: The Watch List
The Problem Without Dependencies
Without telling useEffect when to run, it runs every single time your screen updates. Thatâs like your crew doing the same task over and overâwasteful!
What Are Dependencies?
Dependencies are a watch list. You tell React: âOnly run this effect when THESE things change.â
graph TD A["Component Renders"] --> B{Check Dependencies} B -->|Changed| C["Run Effect"] B -->|Same| D["Skip Effect"] C --> E["Continue"] D --> E
The Three Dependency Options
1. Empty Array [] â Run Once
useEffect(() => {
console.log('I run only ONCE!');
}, []); // Empty = no dependencies
Perfect for: Initial data fetch, one-time setup
2. With Dependencies [value] â Run When Value Changes
useEffect(() => {
console.log(`User ID changed to: ${userId}`);
}, [userId]); // Runs when userId changes
Perfect for: Reacting to specific state changes
3. No Array â Run Every Render
useEffect(() => {
console.log('I run EVERY TIME!');
}); // Dangerous! Avoid unless needed
Real Life Example: Search Feature
function SearchScreen() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
// Only search when query changes
if (query.length > 2) {
fetch(`/api/search?q=${query}`)
.then(res => res.json())
.then(data => setResults(data));
}
}, [query]); // Watch the query!
return (
<View>
<TextInput
value={query}
onChangeText={setQuery}
/>
{/* Show results */}
</View>
);
}
đ§č useEffect Cleanup: The Cleaning Crew
Why Cleanup Matters
Imagine your movie crew sets up lights for a scene. When the scene ends, they need to turn off the lights. Otherwise, power keeps running!
In apps, âleaving lights onâ means:
- Timers still ticking
- Event listeners still listening
- Subscriptions still active
This causes memory leaks and bugs! đ„
How Cleanup Works
useEffect(() => {
// SETUP: Turn on the lights
const timer = setInterval(() => {
console.log('Tick!');
}, 1000);
// CLEANUP: Turn off the lights
return () => {
clearInterval(timer);
};
}, []);
The function you return from useEffect is your cleanup crew!
graph TD A["Effect Runs"] --> B["Setup Code Executes"] B --> C["Component Unmounts<br>OR Dependencies Change"] C --> D["Cleanup Function Runs"] D --> E["New Effect Runs<br>if still mounted"]
Real Life Example: Live Timer
function TimerScreen() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
// Setup: Start timer
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
// Cleanup: Stop timer when leaving
return () => {
clearInterval(interval);
};
}, []); // Empty = setup once
return (
<View>
<Text>Time: {seconds}s</Text>
</View>
);
}
Cleanup with Event Listeners
function OrientationScreen() {
const [orientation, setOrientation] = useState('portrait');
useEffect(() => {
// Setup: Listen for changes
const handler = (event) => {
setOrientation(event.orientation);
};
Dimensions.addEventListener('change', handler);
// Cleanup: Stop listening
return () => {
Dimensions.removeEventListener('change', handler);
};
}, []);
return <Text>Mode: {orientation}</Text>;
}
⥠useLayoutEffect: The Lightning-Fast Crew
When useEffect Isnât Fast Enough
Remember: useEffect runs after the screen is painted. But sometimes you need to measure or change something before the user sees it.
Thatâs useLayoutEffect!
The Difference
| useEffect | useLayoutEffect | |
|---|---|---|
| When | After paint | Before paint |
| User sees | Old â New (flicker) | Just the final result |
| Use for | Data fetching, timers | Measuring, DOM changes |
graph LR A["Render"] --> B["Paint Screen"] B --> C["useEffect"] D["Render"] --> E["useLayoutEffect"] E --> F["Paint Screen"]
Real Life Example: Measuring Elements
import { useLayoutEffect, useRef, useState } from 'react';
import { View, Text } from 'react-native';
function MeasuredBox() {
const boxRef = useRef(null);
const [height, setHeight] = useState(0);
useLayoutEffect(() => {
// Measure BEFORE user sees
boxRef.current.measure((x, y, w, h) => {
setHeight(h);
});
}, []);
return (
<View>
<View ref={boxRef}>
<Text>Content here</Text>
</View>
<Text>Box height: {height}px</Text>
</View>
);
}
When to Use useLayoutEffect
â Measuring element sizes â Scrolling to a position â Animations that need measurements â Preventing visual flicker
â Data fetching (use useEffect) â Timers (use useEffect) â Most other side effects
đ Layout Events: Knowing When Things Change
What Are Layout Events?
When a componentâs size or position changes, React Native can tell you! Itâs like having a spy that reports: âHey, this box just got bigger!â
The onLayout Event
Every View can have an onLayout prop:
function ResponsiveBox() {
const [dimensions, setDimensions] = useState({
width: 0,
height: 0
});
const handleLayout = (event) => {
const { width, height } = event.nativeEvent.layout;
setDimensions({ width, height });
};
return (
<View onLayout={handleLayout} style={{ flex: 1 }}>
<Text>Width: {dimensions.width}px</Text>
<Text>Height: {dimensions.height}px</Text>
</View>
);
}
What onLayout Gives You
event.nativeEvent.layout = {
x: 0, // Position from left
y: 0, // Position from top
width: 350, // Element width
height: 200 // Element height
}
Real Life Example: Responsive Image
function ResponsiveImage() {
const [containerWidth, setContainerWidth] = useState(0);
return (
<View
style={{ flex: 1 }}
onLayout={(e) => {
setContainerWidth(e.nativeEvent.layout.width);
}}
>
<Image
source={{ uri: 'https://...' }}
style={{
width: containerWidth,
height: containerWidth * 0.6 // 60% ratio
}}
/>
</View>
);
}
Combining Layout Events with useLayoutEffect
function SmartComponent() {
const [size, setSize] = useState({ w: 0, h: 0 });
const [adjusted, setAdjusted] = useState(false);
useLayoutEffect(() => {
// React to size changes BEFORE paint
if (size.w > 300) {
setAdjusted(true);
}
}, [size.w]);
return (
<View
onLayout={(e) => {
const { width, height } = e.nativeEvent.layout;
setSize({ w: width, h: height });
}}
style={adjusted ? styles.wide : styles.narrow}
>
<Text>Responsive content</Text>
</View>
);
}
đŹ Putting It All Together
Hereâs our movie analogy in full:
| Concept | Movie Analogy | When to Use |
|---|---|---|
| useEffect | Crew tasks after scene is shot | Data fetching, timers, subscriptions |
| Dependencies | âOnly redo if THIS changesâ | Control when effects run |
| Cleanup | Turning off lights after wrap | Prevent memory leaks |
| useLayoutEffect | Set changes before camera rolls | Measurements, flicker prevention |
| Layout Events | âThe stage just changed size!â | Responsive layouts |
The Golden Rules
- Always add dependencies â Donât let effects run wild
- Always cleanup â Turn off the lights!
- Use useLayoutEffect sparingly â Only for visual measurements
- Use onLayout for responsiveness â Know your container sizes
đ Quick Reference
// Run once on mount
useEffect(() => {
// setup
return () => { /* cleanup */ };
}, []);
// Run when value changes
useEffect(() => {
// react to change
}, [value]);
// Measure before paint
useLayoutEffect(() => {
// measure/adjust
}, []);
// React to size changes
<View onLayout={(e) => {
const { width, height } = e.nativeEvent.layout;
}}>
Youâre now ready to direct your appâs behind-the-scenes crew like a pro! đŹ
Effects might seem complex at first, but remember: theyâre just your helpful crew handling tasks at the right time. Setup when needed, cleanup when done, and always tell them what to watch!
