🔄 What Transfers From React
Good news: most of what you know still applies!
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Identify React concepts that transfer directly to React Native
- Recognize which areas require new learning or adaptation
- Apply your existing hooks knowledge in a mobile context
- Understand how state management patterns carry over
- Adjust your mental model for touch-based event handling
⏱️ Estimated Time: 30-40 minutes
📑 In This Lesson
The Transfer Overview
Here's the encouraging truth: you already know more React Native than you think. The core of React — the mental model, the patterns, the hooks — all of it applies. You're not starting from scratch; you're adding a new rendering target to skills you already have.
Think of it like a musician who plays piano learning to play keyboards. The music theory, rhythm, hand coordination — all that transfers. You just need to learn where the new buttons are and what sounds they make.
Most of your React knowledge transfers directly or with minor adjustments
📊 The Bottom Line
Roughly 70% of your React knowledge applies directly to React Native. Another 15% needs minor adjustments. Only about 15% is genuinely new territory.
What Transfers Completely
Let's celebrate the good stuff first. These React concepts work exactly the same in React Native:
Components and Props
The fundamental building blocks haven't changed. You still create functional components, pass props, and compose them together:
// This works identically in React and React Native!
function Greeting({ name, isLoggedIn }) {
return (
<View>
<Text>Hello, {name}!</Text>
{isLoggedIn && <Text>Welcome back!</Text>}
</View>
);
}
// Usage
<Greeting name="Sarah" isLoggedIn={true} />
All the Hooks You Love
Every React hook works in React Native. No exceptions:
import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
function Counter() {
// ✅ useState - exactly the same
const [count, setCount] = useState(0);
// ✅ useEffect - exactly the same
useEffect(() => {
console.log('Count changed:', count);
}, [count]);
// ✅ useRef - exactly the same
const renderCount = useRef(0);
renderCount.current++;
// ✅ useMemo - exactly the same
const doubleCount = useMemo(() => count * 2, [count]);
// ✅ useCallback - exactly the same
const increment = useCallback(() => {
setCount(c => c + 1);
}, []);
return (
<View>
<Text>Count: {count}</Text>
<Text>Double: {doubleCount}</Text>
<Pressable onPress={increment}>
<Text>Increment</Text>
</Pressable>
</View>
);
}
✅ All These Hooks Work
useState— local component stateuseEffect— side effects and lifecycleuseContext— consuming contextuseReducer— complex state logicuseCallback— memoized callbacksuseMemo— memoized valuesuseRef— mutable refsuseLayoutEffect— synchronous effectsuseImperativeHandle— customizing refsuseDebugValue— dev tools labels
Context API
Global state with Context works identically:
// ThemeContext.js - exactly the same pattern!
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [isDark, setIsDark] = useState(false);
const toggle = () => setIsDark(prev => !prev);
return (
<ThemeContext.Provider value={{ isDark, toggle }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Usage in a component
function SettingsScreen() {
const { isDark, toggle } = useTheme();
return (
<View>
<Text>Dark Mode: {isDark ? 'On' : 'Off'}</Text>
<Switch value={isDark} onValueChange={toggle} />
</View>
);
}
Custom Hooks
Your custom hooks? They'll probably work with zero changes:
// This custom hook works in both React and React Native!
function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(() => {
setValue(v => !v);
}, []);
const setTrue = useCallback(() => setValue(true), []);
const setFalse = useCallback(() => setValue(false), []);
return { value, toggle, setTrue, setFalse };
}
// Works perfectly in React Native
function ExpandableCard() {
const { value: isExpanded, toggle } = useToggle(false);
return (
<View>
<Pressable onPress={toggle}>
<Text>{isExpanded ? 'Collapse' : 'Expand'}</Text>
</Pressable>
{isExpanded && <Text>Hidden content revealed!</Text>}
</View>
);
}
Conditional and List Rendering
JSX patterns are unchanged:
function TaskList({ tasks, isLoading, error }) {
// Conditional rendering - same as web!
if (isLoading) {
return <ActivityIndicator />;
}
if (error) {
return <Text style={styles.error}>{error.message}</Text>;
}
if (tasks.length === 0) {
return <Text>No tasks yet!</Text>;
}
// List rendering - same pattern, but use FlatList for long lists
return (
<View>
{tasks.map(task => (
<View key={task.id}>
<Text>{task.title}</Text>
{task.completed && <Text>✓</Text>}
</View>
))}
</View>
);
}
What Transfers with Tweaks
These concepts carry over, but the syntax or API differs slightly. It's like speaking British English vs American English — same language, slightly different vocabulary.
Event Handling
The concept is identical (functions that respond to user actions), but the event names and properties change:
// 🌐 React (Web)
<button onClick={handleClick}>Click me</button>
<input onChange={handleChange} onFocus={handleFocus} />
<div onMouseEnter={handleHover} />
// 📱 React Native
<Pressable onPress={handlePress}>
<Text>Press me</Text>
</Pressable>
<TextInput onChangeText={handleChange} onFocus={handleFocus} />
// No mouse events! Touch only.
Event Name Mapping
| Web Event | React Native Event | Notes |
|---|---|---|
onClick |
onPress |
Touch equivalent of click |
onChange |
onChangeText |
Gets text string directly, not event |
onSubmit |
onSubmitEditing |
When keyboard "done" is pressed |
onKeyDown |
onKeyPress |
Limited support |
onMouseEnter/Leave |
❌ Not available | No hover on touchscreens |
onScroll |
onScroll |
Same name, different event shape |
Forms and Controlled Inputs
The controlled component pattern works the same; only the component and prop names differ:
// 🌐 React (Web)
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
);
}
// 📱 React Native
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<View>
<TextInput
keyboardType="email-address"
value={email}
onChangeText={setEmail} // Gets string directly!
placeholder="Email"
/>
<TextInput
secureTextEntry // Instead of type="password"
value={password}
onChangeText={setPassword}
placeholder="Password"
/>
<Pressable onPress={handleSubmit}>
<Text>Login</Text>
</Pressable>
</View>
);
}
💡 Nice Improvement
Notice how onChangeText gives you the string directly instead of e.target.value? React Native often has more convenient APIs for common mobile patterns!
State Management Libraries
Redux, Zustand, MobX, Jotai — they all work with zero changes:
// Zustand store - identical to web usage
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
// Use it in React Native - exactly the same!
function Counter() {
const { count, increment, decrement } = useStore();
return (
<View>
<Text>{count}</Text>
<Pressable onPress={increment}><Text>+</Text></Pressable>
<Pressable onPress={decrement}><Text>-</Text></Pressable>
</View>
);
}
Data Fetching
The fetch API and libraries like React Query work the same:
// React Query - works identically in React Native!
import { useQuery } from '@tanstack/react-query';
function UserProfile({ userId }) {
const { data, isLoading, error } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),
});
if (isLoading) return <ActivityIndicator />;
if (error) return <Text>Error: {error.message}</Text>;
return (
<View>
<Text>{data.name}</Text>
<Text>{data.email}</Text>
</View>
);
}
What You Need to Relearn
Okay, here's the part that actually requires new learning. These areas use different paradigms or entirely new APIs:
🎨 Styling
This is the biggest adjustment. No CSS files, no class names, no cascade:
flowchart LR
subgraph Web["Web Styling"]
A["CSS Files"] --> B["Class Names"]
B --> C["Cascade & Inheritance"]
C --> D["Media Queries"]
end
subgraph RN["React Native Styling"]
E["StyleSheet.create()"] --> F["Style Objects"]
F --> G["No Cascade"]
G --> H["Dimensions API"]
end
style Web fill:#ffebee,stroke:#c62828
style RN fill:#e8f5e9,stroke:#388e3c
// Web CSS
.card {
background-color: white;
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
// React Native StyleSheet
const styles = StyleSheet.create({
card: {
backgroundColor: 'white', // camelCase
padding: 16, // number, not '16px'
borderRadius: 8,
// Shadows are complex in RN
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3, // Android shadow
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
});
⚠️ Key Differences
- No units — numbers represent density-independent pixels
- No shorthand — use
marginTop, notmargin-topin shorthand - Limited properties — not all CSS properties exist
- Flexbox is default — and
flexDirectiondefaults tocolumn
🧭 Navigation
Web routing (React Router) doesn't apply. Mobile navigation is stack-based and fundamentally different:
flowchart TB
subgraph Web["Web Navigation"]
A["URL-based"] --> B["Browser History"]
B --> C["React Router"]
end
subgraph Mobile["Mobile Navigation"]
D["Stack-based"] --> E["Screen Transitions"]
E --> F["Expo Router or React Navigation"]
F --> G["Tabs, Stacks, Drawers"]
end
style Web fill:#e3f2fd,stroke:#1976d2
style Mobile fill:#e8f5e9,stroke:#388e3c
// Web - React Router
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/profile">Profile</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
</Routes>
</BrowserRouter>
);
}
// React Native - Expo Router (file-based)
// app/
// _layout.tsx ← Root layout
// index.tsx ← Home screen (/)
// profile.tsx ← Profile screen (/profile)
// settings/
// index.tsx ← Settings screen (/settings)
// Navigation happens via Link or router
import { Link, router } from 'expo-router';
function HomeScreen() {
return (
<View>
<Link href="/profile">Go to Profile</Link>
<Pressable onPress={() => router.push('/settings')}>
<Text>Settings</Text>
</Pressable>
</View>
);
}
📱 Core Components
You need to learn the new component vocabulary (covered in Module 3):
| Concept | What to Learn |
|---|---|
| Containers | View, ScrollView, SafeAreaView |
| Text | Text (and its nesting rules) |
| Images | Image, ImageBackground |
| Lists | FlatList, SectionList (virtualized) |
| Buttons | Pressable, Button |
| Inputs | TextInput, Switch |
📷 Platform APIs
Accessing device features is completely new territory:
- Camera —
expo-camera - Location —
expo-location - Storage —
AsyncStorageorexpo-secure-store - Notifications —
expo-notifications - Haptics —
expo-haptics
These have no web equivalent and will be covered in Module 8.
✨ Animations
CSS animations and transitions don't exist. You'll learn:
AnimatedAPI (built-in)React Native Reanimated(better performance)LayoutAnimation(simple layout changes)
These are covered in Module 9.
Side-by-Side Examples
Let's look at a complete component to see the differences in context:
A Todo Item Component
// 🌐 React (Web) Version
import { useState } from 'react';
import './TodoItem.css';
function TodoItem({ todo, onToggle, onDelete }) {
const [isEditing, setIsEditing] = useState(false);
return (
<div className={`todo-item ${todo.completed ? 'completed' : ''}`}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span className="todo-text">{todo.text}</span>
<button onClick={() => onDelete(todo.id)}>
Delete
</button>
</div>
);
}
// 📱 React Native Version
import { useState } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';
import Checkbox from 'expo-checkbox';
function TodoItem({ todo, onToggle, onDelete }) {
const [isEditing, setIsEditing] = useState(false); // Same!
return (
<View style={[
styles.todoItem,
todo.completed && styles.completed // Conditional styles
]}>
<Checkbox
value={todo.completed}
onValueChange={() => onToggle(todo.id)}
/>
<Text style={styles.todoText}>{todo.text}</Text>
<Pressable onPress={() => onDelete(todo.id)}>
<Text style={styles.deleteButton}>Delete</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
todoItem: {
flexDirection: 'row',
alignItems: 'center',
padding: 12,
backgroundColor: '#fff',
borderRadius: 8,
marginBottom: 8,
},
completed: {
opacity: 0.5,
},
todoText: {
flex: 1,
marginLeft: 12,
fontSize: 16,
},
deleteButton: {
color: '#ef4444',
fontWeight: '600',
},
});
✅ What Stayed the Same
- Component structure and props
useStatehook- Conditional rendering logic
- Event handler patterns
- Composition approach
⚡ What Changed
div→Viewspan→Textbutton→Pressable+TextclassName→styleonClick→onPress- CSS file →
StyleSheet.create()
Summary
🎉 Key Takeaways
- ~70% of React knowledge transfers directly — components, props, state, hooks, context, and patterns
- Event handling is similar — just different event names (
onPressvsonClick) - State management libraries work unchanged — Redux, Zustand, MobX all work identically
- Styling requires the biggest adjustment — StyleSheet objects instead of CSS
- Navigation is a new paradigm — stack-based, not URL-based
- Platform APIs are new territory — camera, location, etc. will be learned fresh
🚀 What's Next?
Now that you know what transfers, let's complete your mental map of the React Native world. In the next lesson, we'll survey the React Native ecosystem — the tools, libraries, and resources that you'll be working with throughout this course.
💪 You're Not Starting Over!
Your React foundation is solid and valuable. You're building on top of it, not replacing it. The learning curve is shorter than you might fear!