Skip to main content

⚡ Development Workflow

Master the tools and techniques for productive React Native development

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Understand how Metro bundler works and what it does
  • Use the developer menu and debugging tools effectively
  • Read and interpret React Native error messages
  • Debug your app using console logs and the debugger
  • Apply productivity shortcuts and best practices

⏱️ Estimated Time: 30-40 minutes

📑 In This Lesson

Understanding Metro Bundler

When you run npx expo start, you start Metro — the JavaScript bundler for React Native. Think of Metro as the engine that powers your development experience. Understanding it helps you debug issues and optimize your workflow.

What Metro Does

flowchart LR
    subgraph Input["Your Code"]
        A[".tsx files"]
        B[".ts files"]
        C["Assets"]
        D["node_modules"]
    end
    
    subgraph Metro["Metro Bundler"]
        E["Transform
(Babel)"] F["Bundle
(Combine)"] G["Serve
(HTTP)"] end subgraph Output["To Device"] H["JavaScript
Bundle"] end A --> E B --> E C --> F D --> E E --> F F --> G G --> H style Metro fill:#fff3e0,stroke:#ff9800

Metro transforms, bundles, and serves your code to the device

Metro's responsibilities include:

Task What It Does
Transform Converts TypeScript/JSX to plain JavaScript using Babel
Bundle Combines all your files into a single JavaScript bundle
Serve Runs an HTTP server that devices connect to
Watch Monitors files for changes and triggers hot reload
Cache Caches transformed files for faster rebuilds

The Metro Terminal Interface

When Metro is running, you have access to keyboard shortcuts:

› Press a │ open Android

› Press i │ open iOS simulator

› Press w │ open web

› Press j │ open debugger

› Press r │ reload app

› Press m │ toggle menu

› Press o │ open project in editor

› Press c │ show QR code

› Press ? │ show all commands

Common Metro Issues

Metro stuck or not updating

Solution: Clear the cache and restart:

# Stop Metro (Ctrl+C), then:
npx expo start --clear
Port 8081 already in use

Solution: Use a different port or kill the existing process:

# Use a different port
npx expo start --port 8082

# Or find and kill the process using 8081
# Mac/Linux:
lsof -i :8081
kill -9 <PID>

# Windows:
netstat -ano | findstr :8081
taskkill /PID <PID> /F
Bundle takes forever to load

Causes and solutions:

  • First load: Normal — Metro caches for subsequent loads
  • Large dependencies: Check for unnecessary imports
  • Slow network: Use local connection instead of tunnel

The Developer Menu

The developer menu is your control center while running the app. It gives you access to debugging tools, reload options, and performance monitoring.

Opening the Developer Menu

Platform How to Open
iOS Simulator Cmd + D or shake gesture
Android Emulator Cmd + M (Mac) / Ctrl + M (Windows) or shake
Physical Device Shake the device, or use three-finger long press
From Terminal Press m in Metro terminal

Developer Menu Options

Developer Menu 🔄 Reload 🐛 Open JS Debugger ⚡ Toggle Fast Refresh 📊 Show Performance Monitor 🔍 Show Element Inspector

The developer menu provides quick access to debugging tools

Menu Options Explained

Option What It Does When to Use
Reload Full reload of the JavaScript bundle When hot reload doesn't pick up changes
Open JS Debugger Opens Chrome DevTools for debugging Setting breakpoints, inspecting state
Toggle Fast Refresh Enable/disable hot reloading Usually keep ON; toggle if issues
Performance Monitor Shows FPS, memory, and JS thread info Diagnosing performance issues
Element Inspector Tap elements to see their styles Debugging layout and styling

✅ Fast Refresh vs Full Reload

  • Fast Refresh (Hot Reload): Updates only changed components, preserves state
  • Full Reload: Reloads entire app, resets all state

Fast Refresh works for most changes. Use Full Reload when things get out of sync.

Debugging Techniques

Effective debugging is a crucial skill. React Native offers several approaches, from simple console logs to full-featured debuggers.

Technique 1: Console Logging

The simplest and often most effective debugging method:

function MyComponent() {
  const [count, setCount] = useState(0);
  
  // Log state changes
  console.log('Render with count:', count);
  
  const handlePress = () => {
    console.log('Button pressed!');
    console.log('Previous count:', count);
    setCount(count + 1);
  };
  
  // Debug object data
  const user = { name: 'John', id: 123 };
  console.log('User object:', JSON.stringify(user, null, 2));
  
  // Use different log levels
  console.info('Info message');
  console.warn('Warning message');  // Shows yellow box
  console.error('Error message');   // Shows red box
  
  return (
    <Pressable onPress={handlePress}>
      <Text>Count: {count}</Text>
    </Pressable>
  );
}

Where to see logs:

  • Metro terminal (where you ran npx expo start)
  • Browser console (if using JS debugger)
  • Flipper or React Native Debugger

💡 Pro Tip: Labeled Logs

Prefix your logs to find them easily:

console.log('[MyComponent] State updated:', state);
console.log('[API] Response:', response);
console.log('[Navigation] Navigating to:', screen);

Technique 2: JavaScript Debugger

For more complex debugging, use the JavaScript debugger:

  1. Press j in Metro terminal (or use developer menu)
  2. A browser window opens with Chrome DevTools
  3. Go to the Sources tab
  4. Find your file and set breakpoints
sequenceDiagram
    participant App as 📱 App
    participant Metro as ⚡ Metro
    participant Browser as 🌐 Chrome DevTools
    
    App->>Metro: Press 'j' or "Open Debugger"
    Metro->>Browser: Open debugger session
    Browser->>Browser: Load source maps
    Note over Browser: Set breakpoints in Sources tab
    App->>Browser: Execution hits breakpoint
    Browser->>Browser: Inspect variables, step through code
    Browser->>App: Resume execution
                

The JavaScript debugger connects your app to Chrome DevTools

Using Breakpoints

function calculateTotal(items) {
  // You can also use 'debugger' statement
  debugger;  // Execution pauses here when debugger is open
  
  let total = 0;
  for (const item of items) {
    total += item.price;
  }
  return total;
}

Technique 3: React DevTools

Inspect your React component tree, props, and state:

# Install React DevTools globally
npm install -g react-devtools

# Run it (in a separate terminal)
react-devtools

Then shake your device and select "Debug with React DevTools".

Technique 4: Element Inspector

For layout and style debugging:

  1. Open developer menu
  2. Select "Show Element Inspector"
  3. Tap any element on screen
  4. View its styles, dimensions, and hierarchy

⚠️ Debugging Performance Impact

Running with the debugger attached slows down your app significantly. This is normal — disable debugging when testing performance.

Reading Error Messages

React Native error messages can be intimidating, but they follow patterns. Learning to read them quickly will save you hours of debugging.

Anatomy of an Error

Error: Text strings must be rendered within a <Text> component. ① Error message This error is located at: in MyComponent (at index.tsx:15) in View (at MyComponent.tsx:8) in RCTView in App (at renderApplication.js:50) ② File & line number 13 │ return ( 14 │ <View> 15 │ Hello World ← Problem here! ③ Code causing error Press Cmd+D to dismiss or R to reload

React Native errors show the message, location, and offending code

Common Errors and Fixes

Text strings must be rendered within a <Text> component

Cause: Raw text outside a <Text> component

// ❌ Wrong
<View>
  Hello World
</View>

// ✅ Correct
<View>
  <Text>Hello World</Text>
</View>
Cannot read property 'X' of undefined

Cause: Accessing a property on an undefined value

// ❌ user is undefined
<Text>{user.name}</Text>

// ✅ Use optional chaining
<Text>{user?.name}</Text>

// ✅ Or provide default
<Text>{user?.name ?? 'Guest'}</Text>
Invalid hook call

Cause: Using hooks outside a component or conditionally

// ❌ Hook inside condition
if (someCondition) {
  const [state, setState] = useState();  // Wrong!
}

// ✅ Hook at top level
const [state, setState] = useState();
if (someCondition) {
  // use state here
}
Each child in a list should have a unique "key" prop

Cause: Missing or duplicate keys in list rendering

// ❌ No key
{items.map(item => <Text>{item.name}</Text>)}

// ❌ Using index as key (not ideal)
{items.map((item, index) => <Text key={index}>{item.name}</Text>)}

// ✅ Using unique ID
{items.map(item => <Text key={item.id}>{item.name}</Text>)}
Module not found / Cannot find module

Causes and fixes:

  • Typo in import path → Check spelling
  • Package not installed → Run npm install package-name
  • Metro cache stale → Run npx expo start --clear

Red Box vs Yellow Box

Type Severity Action
🔴 Red Box Fatal error — app crashed Must fix before continuing
🟡 Yellow Box Warning — app still works Should fix, but can dismiss

Productivity Tips

These tips and shortcuts will significantly speed up your development workflow.

Keyboard Shortcuts

Shortcut Action Where
r Reload app Metro terminal
m Open developer menu Metro terminal
j Open JavaScript debugger Metro terminal
Cmd/Ctrl + D Open developer menu iOS Simulator
Cmd/Ctrl + M Open developer menu Android Emulator
Cmd/Ctrl + R Reload Simulator/Emulator

VS Code Snippets

If you installed the React Native snippets extension, use these shortcuts:

Snippet Output
rnfc React Native Functional Component
rnfs React Native Functional Component with StyleSheet
imrn import { } from 'react-native'
usf const [state, setState] = useState()
uef useEffect(() => { }, [])

Development Best Practices

✅ Do These Things

  • Keep Metro running — Don't restart for every change
  • Use TypeScript — Catches errors before runtime
  • Test on both platforms — Regularly check iOS and Android
  • Use source control — Commit frequently with Git
  • Clear cache when stucknpx expo start --clear

❌ Avoid These Things

  • Ignoring warnings — They become errors later
  • Console.log in production — Remove or disable before release
  • Testing only on one platform — Bugs hide in the other
  • Skipping TypeScript types — Leads to runtime errors
  • Not reading error messages — They usually tell you the fix!

Workflow Diagram

flowchart TD
    A[Start Development] --> B[npx expo start]
    B --> C{Choose Target}
    C -->|Phone| D[Scan QR with Expo Go]
    C -->|Simulator| E[Press 'i' or 'a']
    D --> F[App Running]
    E --> F
    F --> G[Write Code]
    G --> H[Save File]
    H --> I{Hot Reload Works?}
    I -->|Yes| G
    I -->|No| J[Press 'r' to reload]
    J --> G
    G --> K{Error?}
    K -->|Yes| L[Read Error Message]
    L --> M[Fix Code]
    M --> G
    K -->|No| G
    
    style F fill:#e8f5e9,stroke:#4caf50
    style L fill:#ffebee,stroke:#f44336
                

The typical development loop — code, save, see changes, repeat

Summary

🎉 Key Takeaways

  • Metro bundler transforms and serves your code — use --clear when stuck
  • Developer menu gives access to reload, debugger, and inspector tools
  • Console.log is your first debugging tool — check Metro terminal for output
  • JavaScript debugger lets you set breakpoints and inspect variables
  • Error messages tell you what's wrong — read the message, file, and line number
  • Learn keyboard shortcuts to navigate faster

Quick Reference Card

Metro Terminal:

r = reload | m = menu | j = debugger | c = QR code


Developer Menu:

iOS: Cmd+D | Android: Cmd+M | Device: Shake


When Stuck:

npx expo start --clear

🚀 What's Next?

You now have all the tools and knowledge to develop efficiently. In the final lesson of this module, we'll dive into TypeScript essentials for React Native — the types and patterns you'll use every day.

🛠️ Workflow Mastered!

You now know how to develop, debug, and troubleshoot like a pro. The remaining friction will disappear with practice!