๐ฏ Advanced State Patterns in React Native
The Family Communication System
Imagine your React Native app is like a big family living in a house. Each room is a component, and the family members need to share information with each other. Today, weโll learn four powerful ways to manage this communication!
๐ Our Universal Analogy: The Family House
Think of your app like a house with rooms:
- Parent rooms = Parent components (like the living room)
- Child rooms = Child components (like bedrooms)
- Shared information = State that everyone needs
- House rules = Patterns for managing state
1๏ธโฃ Lifting State Up
The Story: The TV Remote Problem
Imagine two kids (Sarah and Tom) each have a bedroom. They both want to watch the same TV channel, but theyโre fighting about who controls the remote!
The Problem:
- Sarah changes the channel in her room
- Tom doesnโt know what happened
- Theyโre watching different things!
The Solution: Put the remote in the living room (parent). Now both kids ask Mom to change the channel, and everyone sees the same thing!
graph TD A["๐ Living Room<br/>Parent holds TV channel state"] --> B["๐๏ธ Sarah&#39;s Room<br/>Receives channel + asks to change] A --> C[๐๏ธ Tom&#39;s Room<br/>Receives channel + asks to change"]
๐ป Code Example
Before (Bad): Each child has own state
// Sarah's Room - has its own channel
function SarahRoom() {
const [channel, setChannel] =
useState(1);
return (
<Text>Watching: {channel}</Text>
);
}
// Tom's Room - different channel!
function TomRoom() {
const [channel, setChannel] =
useState(5);
return (
<Text>Watching: {channel}</Text>
);
}
After (Good): Parent holds the state
// Living Room - Parent controls TV
function LivingRoom() {
const [channel, setChannel] =
useState(1);
return (
<View>
<SarahRoom
channel={channel}
onChange={setChannel}
/>
<TomRoom
channel={channel}
onChange={setChannel}
/>
</View>
);
}
// Sarah's Room - asks parent
function SarahRoom({ channel, onChange }) {
return (
<View>
<Text>Watching: {channel}</Text>
<Button
title="Next"
onPress={() => onChange(channel + 1)}
/>
</View>
);
}
๐ Key Points
- Lift state UP to the nearest common parent
- Pass state DOWN as props
- Pass update functions DOWN too
- Now both children stay in sync!
2๏ธโฃ Derived State
The Story: The Shopping Cart Total
Imagine you have a shopping cart with items. Each item has a price. You want to show the total price.
Silly Approach: Every time you add an item, manually update a separate โtotalโ number.
Smart Approach: Just calculate the total from the items you already have! Thatโs derived state.
๐ Simple Explanation
Derived State = Information you calculate FROM existing state
Itโs like asking:
- โHow many toys do I have?โ โ Just count your toy box!
- โWhatโs my full name?โ โ Just combine first + last name!
graph TD A["๐ฆ Source State<br/>items array"] --> B["๐งฎ Derived State<br/>total = items.reduce..."] A --> C["๐งฎ Derived State<br/>count = items.length"]
๐ป Code Example
function ShoppingCart() {
// Source State - what we actually store
const [items, setItems] = useState([
{ name: 'Apple', price: 1 },
{ name: 'Bread', price: 3 },
{ name: 'Milk', price: 2 }
]);
// Derived State - calculated, NOT stored!
const totalPrice = items.reduce(
(sum, item) => sum + item.price,
0
);
const itemCount = items.length;
const isEmpty = items.length === 0;
return (
<View>
<Text>Items: {itemCount}</Text>
<Text>Total: ${totalPrice}</Text>
{isEmpty && <Text>Cart is empty!</Text>}
</View>
);
}
๐ซ What NOT To Do
// โ BAD - Don't store derived values!
const [items, setItems] = useState([]);
const [total, setTotal] = useState(0);
const [count, setCount] = useState(0);
// Now you must update 3 things every time!
// Easy to forget and have bugs!
๐ Key Points
- If you can calculate it from existing state, do it!
- Donโt store what you can compute
- Keeps your state simple and bug-free
- Calculated values are always in sync automatically
3๏ธโฃ useReducer Hook
The Story: The Restaurant Kitchen
Imagine a restaurant kitchen. Instead of everyone shouting different orders, thereโs an order system:
- Waiter writes down the order (action)
- Chef (reducer) reads the order and makes the food
- Kitchen state gets updated
This is useReducer! Itโs like having a chef who knows exactly what to do for each type of order.
graph TD A["๐ Action<br/>type: ADD_ITEM"] --> B["๐จโ๐ณ Reducer<br/>Reads action, updates state"] B --> C["๐ฝ๏ธ New State<br/>Updated kitchen inventory"] D["๐ Action<br/>type: REMOVE_ITEM"] --> B
๐ป Basic Pattern
import { useReducer } from 'react';
// The Chef's recipe book
function counterReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
case 'RESET':
return { count: 0 };
default:
return state;
}
}
function Counter() {
// state = current state
// dispatch = send orders to chef
const [state, dispatch] = useReducer(
counterReducer,
{ count: 0 }
);
return (
<View>
<Text>Count: {state.count}</Text>
<Button
title="+1"
onPress={() =>
dispatch({ type: 'INCREMENT' })
}
/>
<Button
title="-1"
onPress={() =>
dispatch({ type: 'DECREMENT' })
}
/>
<Button
title="Reset"
onPress={() =>
dispatch({ type: 'RESET' })
}
/>
</View>
);
}
๐ Real Example: Shopping Cart
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return {
...state,
items: [...state.items, action.item]
};
case 'REMOVE_ITEM':
return {
...state,
items: state.items.filter(
item => item.id !== action.id
)
};
case 'CLEAR_CART':
return { ...state, items: [] };
default:
return state;
}
}
function ShoppingCart() {
const [cart, dispatch] = useReducer(
cartReducer,
{ items: [] }
);
const addApple = () => {
dispatch({
type: 'ADD_ITEM',
item: { id: 1, name: 'Apple', price: 1 }
});
};
return (
<View>
<Text>Items: {cart.items.length}</Text>
<Button title="Add Apple" onPress={addApple} />
<Button
title="Clear"
onPress={() =>
dispatch({ type: 'CLEAR_CART' })
}
/>
</View>
);
}
๐ useState vs useReducer
| useState | useReducer |
|---|---|
| Simple state | Complex state |
| Few updates | Many update types |
| Independent values | Related values |
| Quick setup | More organized |
๐ Key Points
- Reducer = A function that takes state + action, returns new state
- Dispatch = Send actions to trigger updates
- Great for complex state with many update types
- All state logic lives in one place
4๏ธโฃ Conditional Rendering
The Story: The Magic Door
Imagine a magic door that shows different things based on whoโs looking:
- If youโre a kid โ Shows toys
- If youโre an adult โ Shows work stuff
- If no oneโs there โ Shows โWelcome!โ
This is conditional rendering โ showing different things based on conditions!
๐ป Three Ways to Do It
Way 1: If-Else (Outside JSX)
function WelcomeScreen({ user }) {
// Decide what to show BEFORE return
let content;
if (user) {
content = <Text>Hello, {user.name}!</Text>;
} else {
content = <Text>Please log in</Text>;
}
return <View>{content}</View>;
}
Way 2: Ternary Operator (? :)
function WelcomeScreen({ user }) {
return (
<View>
{user ? (
<Text>Hello, {user.name}!</Text>
) : (
<Text>Please log in</Text>
)}
</View>
);
}
Way 3: && Operator (Show or Nothing)
function Notification({ hasMessage }) {
return (
<View>
{hasMessage && (
<Text>๐ You have a new message!</Text>
)}
</View>
);
}
๐จ Visual Guide
graph TD A["Check Condition"] --> B{Is it true?} B -->|Yes| C["Show This โ "] B -->|No| D["Show That โ<br/>or Nothing"]
๐ ๏ธ Real-World Example
function ProfileScreen({ user, isLoading, error }) {
// Loading state
if (isLoading) {
return <ActivityIndicator size="large" />;
}
// Error state
if (error) {
return <Text style={{ color: 'red' }}>
Oops! {error}
</Text>;
}
// No user state
if (!user) {
return <Text>Please log in</Text>;
}
// Success state - show profile
return (
<View>
<Text style={{ fontSize: 24 }}>
{user.name}
</Text>
<Text>{user.email}</Text>
{user.isPremium && (
<Text>โญ Premium Member</Text>
)}
{user.notifications > 0 ? (
<Text>
๐ {user.notifications} new
</Text>
) : (
<Text>No notifications</Text>
)}
</View>
);
}
โ ๏ธ Watch Out!
// โ Bug with numbers!
{count && <Text>Count: {count}</Text>}
// If count = 0, shows "0" on screen!
// โ
Fix it like this:
{count > 0 && <Text>Count: {count}</Text>}
// OR
{count !== 0 && <Text>Count: {count}</Text>}
๐ Key Points
- Ternary (? :) = Show A or B
- && Operator = Show or hide
- If-else = Complex logic before JSX
- Watch out for falsy values like
0!
๐ฏ Quick Summary
| Pattern | When to Use | Example |
|---|---|---|
| Lifting State Up | Multiple children need same data | Shared form, synced filters |
| Derived State | Can calculate from existing state | Totals, counts, filtered lists |
| useReducer | Complex state, many actions | Shopping cart, forms |
| Conditional Rendering | Show different UI based on state | Loading, errors, auth |
๐ You Did It!
Now you understand the four superpowers of state management:
- ๐ Lift state up to share between siblings
- ๐งฎ Derive state instead of storing calculated values
- ๐จโ๐ณ useReducer for complex state with many actions
- ๐ช Conditional rendering to show the right UI
These patterns will make your React Native apps cleaner, faster, and easier to understand!
Remember: Good state management is like a well-organized house โ everyone knows where things are, and nothing gets lost! ๐กโจ
