Examples
Five practical React Native snippets: a list with pull-to-refresh, a form with validation, secure storage, push notifications opt-in, and a small native module call.
Five RN recipes
EXAMPLE
// 1) FlatList with pull-to-refresh + infinite scroll
import { FlatList, RefreshControl, Text, View } from 'react-native';
import { useEffect, useState } from 'react';
export function Orders() {
const [data, setData] = useState<any[]>([]);
const [refreshing, setRefreshing] = useState(false);
const [cursor, setCursor] = useState<string | null>(null);
async function load(reset = false) {
setRefreshing(true);
try {
const url = '/api/orders' + (cursor && !reset ? '?after=' + cursor : '');
const res = await fetch(url);
const next = await res.json();
setData((prev) => (reset ? next.data : [...prev, ...next.data]));
setCursor(next.nextCursor);
} finally { setRefreshing(false); }
}
useEffect(() => { load(true); }, []);
return (
<FlatList
data={data}
keyExtractor={(o) => o.id}
renderItem={({ item }) => (
<View style={{ padding: 12, borderBottomWidth: 1, borderColor: '#eee' }}>
<Text>{item.customer}</Text>
</View>
)}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => load(true)} />}
onEndReached={() => cursor && load(false)}
onEndReachedThreshold={0.3}
/>
);
}
// 2) Form with simple validation
import { TextInput, Button, View, Text } from 'react-native';
export function SignupForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const errors = {
email: /^\S+@\S+\.\S+$/.test(email) ? null : 'Invalid email',
password: password.length >= 8 ? null : 'At least 8 characters',
} as Record<string, string | null>;
const hasErrors = Object.values(errors).some(Boolean);
return (
<View style={{ padding: 16, gap: 8 }}>
<TextInput placeholder='Email' value={email} onChangeText={setEmail} autoComplete='email' />
<Text style={{ color: 'red' }}>{errors.email}</Text>
<TextInput placeholder='Password' value={password} onChangeText={setPassword} secureTextEntry />
<Text style={{ color: 'red' }}>{errors.password}</Text>
<Button title='Sign up' disabled={hasErrors} onPress={() => {/* submit */}} />
</View>
);
}
// 3) Secure storage (Expo SecureStore or react-native-keychain)
import * as SecureStore from 'expo-secure-store';
async function saveToken(token: string) {
await SecureStore.setItemAsync('access_token', token);
}
async function loadToken(): Promise<string | null> {
return SecureStore.getItemAsync('access_token');
}
async function clearToken() {
await SecureStore.deleteItemAsync('access_token');
}
// 4) Push notifications opt-in (Expo Notifications)
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
async function registerForPush(): Promise<string | null> {
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'default', importance: Notifications.AndroidImportance.DEFAULT,
});
}
const { status } = await Notifications.getPermissionsAsync();
let final = status;
if (status !== 'granted') {
const ask = await Notifications.requestPermissionsAsync();
final = ask.status;
}
if (final !== 'granted') return null;
const token = await Notifications.getExpoPushTokenAsync();
return token.data;
}
// 5) Calling a native module (Expo bare / RN turbo module)
// Example: react-native-haptic-feedback
import { trigger } from 'react-native-haptic-feedback';
function HapticButton() {
return (
<Button title='Haptic' onPress={() => trigger('impactMedium')} />
);
}
// ===== Patterns to internalise =====
// - FlatList over ScrollView for any list > ~30 items
// - SecureStore / Keychain for tokens; NEVER AsyncStorage
// - Permission flow: check -> request -> handle deny gracefully
// - Hooks for cross-cutting state (useAuth, useTheme, useApi)
// ===== Pitfalls =====
// - Re-fetching on every focus without de-bouncing -> hammered API
// - Forgetting keyboardAvoidingView -> inputs hidden behind keyboard
// - Storing access tokens in AsyncStorage -> recoverable from device backups
// - Push notification token not refreshed on user change -> stale targeting
Why it matters
Use FlatList (or SectionList) for any list past about 30 items — it virtualises rows, recycles components, and handles pull-to-refresh + infinite scroll cleanly. ScrollView with .map() looks simpler but stalls on long lists; the cost of switching to FlatList is one prop and a key extractor.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Start simple: Login → list → detail. Add navigation, then state, then native APIs.Try it Yourself »
Discussion
Loading…