Advanced State Patterns

Back

Loading concept...

๐ŸŽฏ 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's Room<br/>Receives channel + asks to change] A --> C[๐Ÿ›๏ธ Tom'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&lt;br/&gt;items array"] --> B["๐Ÿงฎ Derived State&lt;br/&gt;total = items.reduce..."] A --> C["๐Ÿงฎ Derived State&lt;br/&gt;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:

  1. Waiter writes down the order (action)
  2. Chef (reducer) reads the order and makes the food
  3. 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&lt;br/&gt;type: ADD_ITEM"] --> B["๐Ÿ‘จโ€๐Ÿณ Reducer&lt;br/&gt;Reads action, updates state"] B --> C["๐Ÿฝ๏ธ New State&lt;br/&gt;Updated kitchen inventory"] D["๐Ÿ“ Action&lt;br/&gt;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 โŒ&lt;br/&gt;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:

  1. ๐Ÿ  Lift state up to share between siblings
  2. ๐Ÿงฎ Derive state instead of storing calculated values
  3. ๐Ÿ‘จโ€๐Ÿณ useReducer for complex state with many actions
  4. ๐Ÿšช 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! ๐Ÿกโœจ

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.