⚡ 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
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:
- Press
jin Metro terminal (or use developer menu) - A browser window opens with Chrome DevTools
- Go to the Sources tab
- 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:
- Open developer menu
- Select "Show Element Inspector"
- Tap any element on screen
- 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
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 stuck —
npx 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
--clearwhen 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!