State Management: Context and Refs in React Native
The Story of the Magic Messenger and the Secret Notebook ๐ฌ๐
Imagine you live in a big castle with many rooms. Every room has family members doing different things. Now, what if everyone needed to know when dinner is ready?
You have two magic tools:
- A Magic Messenger (Context) - Shouts announcements to everyone in the castle at once!
- A Secret Notebook (Refs) - A private note only YOU can write in and peek at, without disturbing anyone.
Letโs explore both!
Part 1: The Magic Messenger (Context) ๐ฐ๐ข
What is useContext Hook?
Think of useContext as a walkie-talkie that lets any room in your castle hear important messagesโwithout passing notes through every single door!
The Problem Without Context:
// Without Context - passing props through
// every component (prop drilling!)
<App>
<Header user={user} />
<Navigation user={user} />
<Avatar user={user} /> // Finally used!
Thatโs like passing a note through 10 people just to reach your friend!
The Solution With Context:
// With Context - direct access!
<UserContext.Provider value={user}>
<App>
<Header />
<Navigation />
<Avatar /> // Gets user directly!
Now Avatar can grab the message straight from the air! โจ
Creating Context
Creating Context is like setting up a radio station. First, you decide what channel youโll broadcast on.
import { createContext } from 'react';
// Create your radio station
const ThemeContext = createContext('light');
// 'light' is the default value
// (what plays if no one is broadcasting)
Simple Analogy:
createContext()= Building the radio tower- Default value = What plays when tower is silent
- Context object = The radio station itself
Context Provider
The Provider is the radio DJ who actually broadcasts messages. Without a DJ, the station plays default music.
import { ThemeContext } from './ThemeContext';
function App() {
const [theme, setTheme] = useState('dark');
return (
// The DJ booth - broadcasting 'dark'
<ThemeContext.Provider value={theme}>
<MainScreen />
<Settings />
</ThemeContext.Provider>
);
}
Everything inside <Provider> can tune in!
graph TD A["๐๏ธ Provider - DJ Booth"] --> B["๐ป MainScreen"] A --> C["๐ป Settings"] B --> D["๐ป Button"] C --> E["๐ป Toggle"] style A fill:#667eea,color:#fff
Using Context in Components
Any component can tune in using useContext:
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
function Button() {
// Tune into the theme station
const theme = useContext(ThemeContext);
return (
<TouchableOpacity
style={{
backgroundColor: theme === 'dark'
? '#333'
: '#fff'
}}
>
<Text>Press Me!</Text>
</TouchableOpacity>
);
}
Context Performance โก
The Gotcha: When the DJ changes the song, EVERYONE listening re-renders!
// โ ๏ธ Problem: Everything re-renders!
<ThemeContext.Provider value={{ theme, user }}>
Solutions:
1. Split Your Contexts:
// Good! Separate stations
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={user}>
<App />
</UserContext.Provider>
</ThemeContext.Provider>
2. Memoize Values:
function App() {
const [theme, setTheme] = useState('dark');
// Only creates new object when theme changes
const value = useMemo(
() => ({ theme, setTheme }),
[theme]
);
return (
<ThemeContext.Provider value={value}>
<Content />
</ThemeContext.Provider>
);
}
Context for Global State
Context shines for app-wide shared data:
// AuthContext.js - Global auth state
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const login = async (email, password) => {
// ... login logic
setUser(userData);
};
const logout = () => setUser(null);
return (
<AuthContext.Provider
value={{ user, loading, login, logout }}
>
{children}
</AuthContext.Provider>
);
}
// Custom hook for easy access
export const useAuth = () => useContext(AuthContext);
Using it anywhere:
function ProfileScreen() {
const { user, logout } = useAuth();
return (
<View>
<Text>Hello, {user.name}!</Text>
<Button title="Logout" onPress={logout} />
</View>
);
}
Common Global State Uses:
- ๐จ Theme (dark/light mode)
- ๐ Authentication (user login state)
- ๐ Language/Localization
- ๐ Shopping cart
Part 2: The Secret Notebook (Refs) ๐๐
What is useRef Hook?
useRef is like a secret notebook that:
- Only YOU can read and write
- Doesnโt tell anyone when you change it
- Remembers everything between renders
import { useRef } from 'react';
function StopwatchScreen() {
// Create a secret notebook
const timerRef = useRef(null);
const startTimer = () => {
// Write in the notebook
timerRef.current = setInterval(() => {
console.log('tick');
}, 1000);
};
const stopTimer = () => {
// Read from the notebook
clearInterval(timerRef.current);
};
return (
<View>
<Button title="Start" onPress={startTimer} />
<Button title="Stop" onPress={stopTimer} />
</View>
);
}
Key Difference from State:
| Feature | useState | useRef |
|---|---|---|
| Changes cause re-render? | โ Yes | โ No |
| Preserved between renders? | โ Yes | โ Yes |
| When to use? | UI data | Silent data |
Accessing DOM/Native Elements
Refs can grab a direct handle to components:
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// Focus the input when screen loads
inputRef.current.focus();
}, []);
return (
<TextInput
ref={inputRef}
placeholder="I auto-focus!"
/>
);
}
Common Uses:
- ๐ Focus an input
- ๐ Scroll to a position
- ๐ Measure element size
- ๐ฌ Play/pause video
Ref Forwarding
The Problem: What if you wrap a component and still need to access its ref?
// This won't work!
function FancyInput(props) {
return <TextInput {...props} />;
}
// Parent can't get the ref!
<FancyInput ref={myRef} />
The Solution - forwardRef:
import { forwardRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
return (
<TextInput
ref={ref} // Pass it through!
style={styles.fancy}
{...props}
/>
);
});
// Now it works!
function Form() {
const inputRef = useRef(null);
return (
<FancyInput
ref={inputRef}
placeholder="Fancy!"
/>
);
}
graph TD A["๐จโ๐ฉโ๐ง Parent Component"] -->|ref| B["๐ฆ FancyInput"] B -->|forwardRef| C["๐ TextInput"] style B fill:#4ECDC4,color:#fff
Think of it like:
- Normal component = Package with no forwarding address
- forwardRef = Package with โplease forward toโ sticker
Imperative Handle
Sometimes you want to control what the parent can do with the ref. Itโs like giving someone a remote control with only specific buttons.
import {
forwardRef,
useRef,
useImperativeHandle
} from 'react';
const VideoPlayer = forwardRef((props, ref) => {
const videoRef = useRef(null);
// Create a custom remote control
useImperativeHandle(ref, () => ({
// Only expose these buttons!
play: () => videoRef.current.play(),
pause: () => videoRef.current.pause(),
seek: (time) => {
videoRef.current.currentTime = time;
}
}));
return <Video ref={videoRef} source={props.src} />;
});
// Parent usage
function Screen() {
const playerRef = useRef(null);
return (
<View>
<VideoPlayer ref={playerRef} src="movie.mp4" />
<Button
title="Play"
onPress={() => playerRef.current.play()}
/>
<Button
title="Skip to 30s"
onPress={() => playerRef.current.seek(30)}
/>
</View>
);
}
Why use Imperative Handle?
- ๐ Hide internal implementation
- ๐ฎ Expose only whatโs needed
- ๐ Create clean, documented APIs
Quick Summary: When to Use What? ๐ฏ
graph TD A{What do you need?} --> B{Share data across<br/>many components?} A --> C{Store value without<br/>re-rendering?} A --> D{Access component<br/>methods/elements?} B -->|Yes| E["โ useContext"] C -->|Yes| F["โ useRef"] D -->|Yes| G["โ useRef + forwardRef"] style E fill:#667eea,color:#fff style F fill:#4ECDC4,color:#fff style G fill:#FF6B6B,color:#fff
| Tool | Use Whenโฆ |
|---|---|
| Context | Many components need same data |
| useRef | Store values silently |
| forwardRef | Pass refs through wrappers |
| useImperativeHandle | Create custom ref APIs |
The Castle Recap ๐ฐ
Remember our castle?
- Context = The castleโs intercom system. Announce once, everyone hears!
- Refs = Your private diary. Write notes, no one gets notified!
- forwardRef = A mail forwarding service for refs
- useImperativeHandle = A custom remote control with only the buttons you choose
Now youโre ready to manage state like a true React Native wizard! ๐งโโ๏ธโจ
