Exercises
Six React Native exercises: navigation, lists, forms, native APIs, persistence.
React Native — exercises
EXAMPLE
// ===== Exercise 1: Tab-based navigation =====
// Stand up an app with three tabs (Home, Browse, Profile) using expo-router.
// Each tab has a single screen with the tab's title.
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
export default function TabLayout() {
return (
<Tabs>
<Tabs.Screen name="index" options={{ title: 'Home' }} />
<Tabs.Screen name="browse" options={{ title: 'Browse' }} />
<Tabs.Screen name="profile" options={{ title: 'Profile' }} />
</Tabs>
);
}
// app/(tabs)/index.tsx + browse.tsx + profile.tsx contain a Text wrapped in View.
// ===== Exercise 2: Long list =====
// Render 10,000 items with FlatList efficiently. Each item shows its index + a button.
import { FlatList, Text, View, Pressable } from 'react-native';
const data = Array.from({ length: 10_000 }, (_, i) => ({ id: i.toString(), n: i }));
<FlatList
data={data}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={{ padding: 12 }}>
<Text>Row #{item.n}</Text>
<Pressable onPress={() => console.log(item.n)}><Text>Tap</Text></Pressable>
</View>
)}
/>
// ===== Exercise 3: Form + validation =====
// Build a sign-in form with email + password, using react-hook-form + zod.
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({ email: z.string().email(), password: z.string().min(8) });
function SignIn() {
const { control, handleSubmit, formState: { errors } } = useForm({ resolver: zodResolver(schema) });
return (
<View>
<Controller control={control} name="email" render={({ field }) => <TextInput {...field} placeholder="Email" />} />
{errors.email && <Text>{errors.email.message}</Text>}
<Pressable onPress={handleSubmit((data) => console.log(data))}><Text>Sign in</Text></Pressable>
</View>
);
}
// ===== Exercise 4: AsyncStorage persistence =====
// Persist a 'theme' preference using AsyncStorage; load on mount.
import AsyncStorage from '@react-native-async-storage/async-storage';
const [theme, setTheme] = useState('light');
useEffect(() => {
AsyncStorage.getItem('theme').then((v) => v && setTheme(v));
}, []);
async function toggleTheme() {
const next = theme === 'light' ? 'dark' : 'light';
setTheme(next);
await AsyncStorage.setItem('theme', next);
}
// ===== Exercise 5: Camera shot =====
// Open the camera with Expo Camera and display the captured photo.
import { CameraView, useCameraPermissions } from 'expo-camera';
const [permission, requestPermission] = useCameraPermissions();
if (!permission?.granted) return <Pressable onPress={requestPermission}><Text>Grant camera</Text></Pressable>;
const cameraRef = useRef<CameraView>(null);
const [uri, setUri] = useState<string>();
async function shoot() {
const photo = await cameraRef.current?.takePictureAsync();
if (photo) setUri(photo.uri);
}
<CameraView ref={cameraRef} style={{ flex: 1 }} />;
<Pressable onPress={shoot}><Text>Shoot</Text></Pressable>;
{uri && <Image source={{ uri }} style={{ width: 100, height: 100 }} />}
// ===== Exercise 6: Pull-to-refresh =====
// Wrap a FlatList with RefreshControl that re-fetches data.
import { RefreshControl } from 'react-native';
const [refreshing, setRefreshing] = useState(false);
const onRefresh = async () => {
setRefreshing(true);
await fetchData();
setRefreshing(false);
};
<FlatList
data={items}
renderItem={...}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
/>
// ===== Patterns =====
// - FlatList for any non-trivial list
// - expo-router for navigation
// - react-hook-form + zod for forms
// - AsyncStorage for small persistence; MMKV for fast key-value
// - Camera / Location via Expo SDK with permission gates
// ===== Pitfalls =====
// - ScrollView for long lists -> jank
// - useState in render without dependency in useEffect -> infinite loop
// - Forgetting Permission gates
// - Strings outside <Text> -> crash
Why it matters
Six React Native exercises drill the daily flows: tabs nav, virtualised lists, forms with validation, AsyncStorage persistence, camera shot with permission gate, pull-to-refresh. The same patterns ship most production app screens.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…