React Native: Een complete mobiele app bouwen in 2026

Uitgebreide gids voor het ontwikkelen van iOS- en Android-apps met React Native. Van setup tot deployment -- alle basisprincipes om te starten.

Mobiele app-ontwikkeling met React Native

React Native maakt het mogelijk om native mobiele applicaties voor iOS en Android te bouwen vanuit een enkele JavaScript-codebase. Het framework wordt onderhouden door Meta en ingezet door apps als Instagram, Facebook en Discord. Het combineert snelle ontwikkelcycli met goed presterende applicaties.

Waarom React Native in 2026?

De nieuwe architectuur (Fabric en TurboModules) is inmiddels stabiel en levert nagenoeg native prestaties met behoud van hoge ontwikkelproductiviteit. Meer dan 40% van de 500 populairste apps maakt gebruik van React Native.

Development Environment Setup

Voordat men begint, moeten de benodigde tools worden geinstalleerd. React Native biedt twee benaderingen: Expo (aanbevolen voor beginners) en de React Native CLI (voor meer controle).

bash
# setup.sh
# Node.js installation (LTS version recommended)
# Check installed version
node --version  # >= 18.x required
npm --version   # >= 9.x required

# Install Expo CLI tool globally
npm install -g expo-cli

# Create a new project with Expo
npx create-expo-app@latest MyApp --template blank-typescript

# Navigate to the project
cd MyApp

# Start the development server
npx expo start

Expo vereenvoudigt het ontwikkelproces aanzienlijk door de native configuratie automatisch af te handelen. Om de app te testen volstaat de Expo Go-app op een smartphone -- de weergegeven QR-code wordt eenvoudig gescand.

bash
# structure.sh
# Generated project structure
MyApp/
├── App.tsx              # Application entry point
├── app.json             # Expo configuration
├── package.json         # Dependencies
├── tsconfig.json        # TypeScript configuration
├── babel.config.js      # Babel configuration
└── assets/              # Images and resources
    ├── icon.png
    └── splash.png

Core React Native Components

React Native biedt native componenten die HTML-elementen vervangen. Elk component wordt rechtstreeks vertaald naar een native iOS- of Android-component.

App.tsxtsx
import React from 'react';
import {
  View,
  Text,
  StyleSheet,
  SafeAreaView,
  StatusBar
} from 'react-native';

// Main application component
export default function App() {
  return (
    // SafeAreaView prevents overlap with status bar
    <SafeAreaView style={styles.container}>
      {/* StatusBar configures system bar appearance */}
      <StatusBar barStyle="dark-content" />

      {/* View is the equivalent of div */}
      <View style={styles.header}>
        {/* Text is required to display text */}
        <Text style={styles.title}>Welcome to MyApp</Text>
        <Text style={styles.subtitle}>
          A React Native application
        </Text>
      </View>
    </SafeAreaView>
  );
}

// StyleSheet.create optimizes styles for native
const styles = StyleSheet.create({
  container: {
    flex: 1,                    // Takes all available space
    backgroundColor: '#ffffff',
  },
  header: {
    padding: 20,
    alignItems: 'center',       // Centers horizontally
    justifyContent: 'center',   // Centers vertically
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#1a1a1a',
    marginBottom: 8,
  },
  subtitle: {
    fontSize: 16,
    color: '#666666',
  },
});
Flexbox als standaard

React Native gebruikt Flexbox met standaard flexDirection: 'column' (in tegenstelling tot het web dat row hanteert). Dit verschil is cruciaal bij het opzetten van layouts.

Handling User Interactions

Touch-interacties worden beheerd via gespecialiseerde componenten. Elk interactietype heeft een eigen component dat is geoptimaliseerd voor prestaties.

components/InteractiveButton.tsxtsx
import React, { useState } from 'react';
import {
  TouchableOpacity,
  TouchableHighlight,
  Pressable,
  Text,
  StyleSheet,
  View,
} from 'react-native';

// Button component with different interaction styles
export function InteractiveButton() {
  const [count, setCount] = useState(0);

  return (
    <View style={styles.container}>
      {/* TouchableOpacity reduces opacity on touch */}
      <TouchableOpacity
        style={styles.button}
        activeOpacity={0.7}
        onPress={() => setCount(c => c + 1)}
      >
        <Text style={styles.buttonText}>
          Counter: {count}
        </Text>
      </TouchableOpacity>

      {/* Pressable offers more control over states */}
      <Pressable
        style={({ pressed }) => [
          styles.button,
          styles.pressableButton,
          pressed && styles.buttonPressed,
        ]}
        onPress={() => console.log('Pressed!')}
        onLongPress={() => console.log('Long press!')}
      >
        {({ pressed }) => (
          <Text style={styles.buttonText}>
            {pressed ? 'Pressed!' : 'Press here'}
          </Text>
        )}
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    gap: 16,
    padding: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    paddingVertical: 14,
    paddingHorizontal: 28,
    borderRadius: 10,
    alignItems: 'center',
  },
  pressableButton: {
    backgroundColor: '#34C759',
  },
  buttonPressed: {
    backgroundColor: '#2DA44E',
    transform: [{ scale: 0.98 }],
  },
  buttonText: {
    color: '#ffffff',
    fontSize: 16,
    fontWeight: '600',
  },
});

Screen Navigation with React Navigation

Navigatie is onmisbaar in elke mobiele app. React Navigation is de standaardoplossing en biedt verschillende navigatietypen die zijn afgestemd op mobiele patronen.

bash
# install-navigation.sh
# Installing navigation dependencies
npx expo install @react-navigation/native
npx expo install @react-navigation/native-stack
npx expo install react-native-screens react-native-safe-area-context
App.tsxtsx
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

// Screen imports
import { HomeScreen } from './screens/HomeScreen';
import { DetailScreen } from './screens/DetailScreen';
import { ProfileScreen } from './screens/ProfileScreen';

// Type definition for TypeScript navigation
export type RootStackParamList = {
  Home: undefined;                    // No parameters
  Detail: { itemId: number; title: string };  // Required parameters
  Profile: { userId?: string };       // Optional parameter
};

// Creating typed navigator
const Stack = createNativeStackNavigator<RootStackParamList>();

export default function App() {
  return (
    // NavigationContainer manages navigation state
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Home"
        screenOptions={{
          headerStyle: { backgroundColor: '#007AFF' },
          headerTintColor: '#ffffff',
          headerTitleStyle: { fontWeight: 'bold' },
        }}
      >
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={{ title: 'Home' }}
        />
        <Stack.Screen
          name="Detail"
          component={DetailScreen}
          options={({ route }) => ({
            title: route.params.title
          })}
        />
        <Stack.Screen
          name="Profile"
          component={ProfileScreen}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}
screens/HomeScreen.tsxtsx
import React from 'react';
import { View, Text, FlatList, TouchableOpacity, StyleSheet } from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { RootStackParamList } from '../App';

// Typing navigation props
type Props = NativeStackScreenProps<RootStackParamList, 'Home'>;

// Sample data
const ITEMS = [
  { id: 1, title: 'First item' },
  { id: 2, title: 'Second item' },
  { id: 3, title: 'Third item' },
];

export function HomeScreen({ navigation }: Props) {
  return (
    <View style={styles.container}>
      {/* FlatList for performant lists */}
      <FlatList
        data={ITEMS}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => (
          <TouchableOpacity
            style={styles.item}
            onPress={() => {
              // Navigation with typed parameters
              navigation.navigate('Detail', {
                itemId: item.id,
                title: item.title,
              });
            }}
          >
            <Text style={styles.itemText}>{item.title}</Text>
          </TouchableOpacity>
        )}
        ItemSeparatorComponent={() => <View style={styles.separator} />}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  item: {
    backgroundColor: '#ffffff',
    padding: 20,
  },
  itemText: {
    fontSize: 16,
    color: '#1a1a1a',
  },
  separator: {
    height: 1,
    backgroundColor: '#e0e0e0',
  },
});

Klaar om je React Native gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

State Management with Context and Hooks

Voor eenvoudig tot gemiddeld state management biedt React Context in combinatie met hooks een doeltreffende oplossing zonder externe afhankelijkheden.

context/AuthContext.tsxtsx
import React, { createContext, useContext, useState, useCallback } from 'react';

// Types for authentication
interface User {
  id: string;
  email: string;
  name: string;
}

interface AuthContextType {
  user: User | null;
  isLoading: boolean;
  signIn: (email: string, password: string) => Promise<void>;
  signOut: () => void;
}

// Creating context with default value
const AuthContext = createContext<AuthContextType | undefined>(undefined);

// Provider that wraps the application
export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  // Sign in function
  const signIn = useCallback(async (email: string, password: string) => {
    setIsLoading(true);
    try {
      // Simulated API call
      const response = await fetch('https://api.example.com/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password }),
      });
      const data = await response.json();
      setUser(data.user);
    } catch (error) {
      console.error('Login error:', error);
      throw error;
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Sign out function
  const signOut = useCallback(() => {
    setUser(null);
  }, []);

  return (
    <AuthContext.Provider value={{ user, isLoading, signIn, signOut }}>
      {children}
    </AuthContext.Provider>
  );
}

// Custom hook to use the context
export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
screens/LoginScreen.tsxtsx
import React, { useState } from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
  ActivityIndicator,
  Alert,
} from 'react-native';
import { useAuth } from '../context/AuthContext';

export function LoginScreen() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { signIn, isLoading } = useAuth();

  // Form submission handler
  const handleSubmit = async () => {
    if (!email || !password) {
      Alert.alert('Error', 'Please fill in all fields');
      return;
    }

    try {
      await signIn(email, password);
    } catch (error) {
      Alert.alert('Error', 'Invalid credentials');
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Login</Text>

      <TextInput
        style={styles.input}
        placeholder="Email"
        value={email}
        onChangeText={setEmail}
        keyboardType="email-address"
        autoCapitalize="none"
        autoComplete="email"
      />

      <TextInput
        style={styles.input}
        placeholder="Password"
        value={password}
        onChangeText={setPassword}
        secureTextEntry
        autoComplete="password"
      />

      <TouchableOpacity
        style={[styles.button, isLoading && styles.buttonDisabled]}
        onPress={handleSubmit}
        disabled={isLoading}
      >
        {isLoading ? (
          <ActivityIndicator color="#ffffff" />
        ) : (
          <Text style={styles.buttonText}>Sign In</Text>
        )}
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
    backgroundColor: '#ffffff',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginBottom: 32,
    textAlign: 'center',
  },
  input: {
    borderWidth: 1,
    borderColor: '#e0e0e0',
    borderRadius: 10,
    padding: 16,
    marginBottom: 16,
    fontSize: 16,
    backgroundColor: '#f9f9f9',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 16,
    borderRadius: 10,
    alignItems: 'center',
    marginTop: 8,
  },
  buttonDisabled: {
    backgroundColor: '#99c9ff',
  },
  buttonText: {
    color: '#ffffff',
    fontSize: 18,
    fontWeight: '600',
  },
});
Globale vs. lokale state

Bij complexere applicaties met uitgebreide gedeelde state kunnen oplossingen als Zustand of Redux Toolkit beter geschikt zijn. Context is optimaal voor state die zelden verandert (thema, authenticatie).

API Calls and Data Management

Communicatie met de backend vormt een kernonderdeel van elke mobiele app. Het volgende patroon maakt gebruik van een abstractielaag voor robuuste API-aanroepen.

services/api.tstsx
// Base API configuration
const API_BASE_URL = 'https://api.example.com';

// Type for API errors
interface ApiError {
  message: string;
  code: string;
  status: number;
}

// Utility function for requests
async function request<T>(
  endpoint: string,
  options: RequestInit = {}
): Promise<T> {
  const url = `${API_BASE_URL}${endpoint}`;

  const config: RequestInit = {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...options.headers,
    },
  };

  try {
    const response = await fetch(url, config);

    if (!response.ok) {
      const error: ApiError = await response.json();
      throw new Error(error.message || 'An error occurred');
    }

    return response.json();
  } catch (error) {
    // Network error handling
    if (error instanceof TypeError) {
      throw new Error('Network connection issue');
    }
    throw error;
  }
}

// Types for entities
interface Product {
  id: string;
  name: string;
  price: number;
  description: string;
  imageUrl: string;
}

// Products service
export const productApi = {
  // Get all products
  getAll: () => request<Product[]>('/products'),

  // Get product by ID
  getById: (id: string) => request<Product>(`/products/${id}`),

  // Create a product
  create: (data: Omit<Product, 'id'>) =>
    request<Product>('/products', {
      method: 'POST',
      body: JSON.stringify(data),
    }),

  // Update a product
  update: (id: string, data: Partial<Product>) =>
    request<Product>(`/products/${id}`, {
      method: 'PATCH',
      body: JSON.stringify(data),
    }),
};
hooks/useProducts.tstsx
import { useState, useEffect, useCallback } from 'react';
import { productApi } from '../services/api';

interface Product {
  id: string;
  name: string;
  price: number;
  description: string;
  imageUrl: string;
}

// Custom hook for product management
export function useProducts() {
  const [products, setProducts] = useState<Product[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  // Initial products loading
  const fetchProducts = useCallback(async () => {
    setIsLoading(true);
    setError(null);

    try {
      const data = await productApi.getAll();
      setProducts(data);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unknown error');
    } finally {
      setIsLoading(false);
    }
  }, []);

  // Refresh (pull-to-refresh)
  const refresh = useCallback(async () => {
    await fetchProducts();
  }, [fetchProducts]);

  // Load on mount
  useEffect(() => {
    fetchProducts();
  }, [fetchProducts]);

  return {
    products,
    isLoading,
    error,
    refresh,
  };
}
screens/ProductListScreen.tsxtsx
import React from 'react';
import {
  View,
  Text,
  FlatList,
  Image,
  StyleSheet,
  RefreshControl,
  ActivityIndicator,
} from 'react-native';
import { useProducts } from '../hooks/useProducts';

export function ProductListScreen() {
  const { products, isLoading, error, refresh } = useProducts();

  // Display during initial loading
  if (isLoading && products.length === 0) {
    return (
      <View style={styles.centered}>
        <ActivityIndicator size="large" color="#007AFF" />
        <Text style={styles.loadingText}>Loading...</Text>
      </View>
    );
  }

  // Display on error
  if (error) {
    return (
      <View style={styles.centered}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <FlatList
      data={products}
      keyExtractor={(item) => item.id}
      contentContainerStyle={styles.list}
      // Pull-to-refresh
      refreshControl={
        <RefreshControl
          refreshing={isLoading}
          onRefresh={refresh}
          tintColor="#007AFF"
        />
      }
      renderItem={({ item }) => (
        <View style={styles.card}>
          <Image
            source={{ uri: item.imageUrl }}
            style={styles.image}
            resizeMode="cover"
          />
          <View style={styles.cardContent}>
            <Text style={styles.productName}>{item.name}</Text>
            <Text style={styles.productPrice}>
              ${item.price.toFixed(2)}
            </Text>
          </View>
        </View>
      )}
    />
  );
}

const styles = StyleSheet.create({
  centered: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  loadingText: {
    marginTop: 12,
    fontSize: 16,
    color: '#666666',
  },
  errorText: {
    fontSize: 16,
    color: '#FF3B30',
    textAlign: 'center',
  },
  list: {
    padding: 16,
  },
  card: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    marginBottom: 16,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  image: {
    width: '100%',
    height: 200,
    borderTopLeftRadius: 12,
    borderTopRightRadius: 12,
  },
  cardContent: {
    padding: 16,
  },
  productName: {
    fontSize: 18,
    fontWeight: '600',
    color: '#1a1a1a',
    marginBottom: 4,
  },
  productPrice: {
    fontSize: 16,
    color: '#007AFF',
    fontWeight: '500',
  },
});

Local Data Storage

Lokale opslag maakt het mogelijk om gegevens tussen sessies te bewaren. AsyncStorage is de standaardoplossing voor eenvoudige data, terwijl SQLite geschikt is voor complexe gestructureerde gegevens.

services/storage.tstsx
import AsyncStorage from '@react-native-async-storage/async-storage';

// Required installation: npx expo install @react-native-async-storage/async-storage

// Centralized storage keys
const STORAGE_KEYS = {
  USER_TOKEN: '@app/user_token',
  USER_PREFERENCES: '@app/user_preferences',
  ONBOARDING_COMPLETE: '@app/onboarding_complete',
} as const;

// Types for user preferences
interface UserPreferences {
  theme: 'light' | 'dark' | 'system';
  notifications: boolean;
  language: string;
}

// Typed storage service
export const storage = {
  // Authentication token
  async getToken(): Promise<string | null> {
    return AsyncStorage.getItem(STORAGE_KEYS.USER_TOKEN);
  },

  async setToken(token: string): Promise<void> {
    await AsyncStorage.setItem(STORAGE_KEYS.USER_TOKEN, token);
  },

  async removeToken(): Promise<void> {
    await AsyncStorage.removeItem(STORAGE_KEYS.USER_TOKEN);
  },

  // User preferences (JSON object)
  async getPreferences(): Promise<UserPreferences | null> {
    const data = await AsyncStorage.getItem(STORAGE_KEYS.USER_PREFERENCES);
    return data ? JSON.parse(data) : null;
  },

  async setPreferences(prefs: UserPreferences): Promise<void> {
    await AsyncStorage.setItem(
      STORAGE_KEYS.USER_PREFERENCES,
      JSON.stringify(prefs)
    );
  },

  // Onboarding
  async isOnboardingComplete(): Promise<boolean> {
    const value = await AsyncStorage.getItem(STORAGE_KEYS.ONBOARDING_COMPLETE);
    return value === 'true';
  },

  async setOnboardingComplete(): Promise<void> {
    await AsyncStorage.setItem(STORAGE_KEYS.ONBOARDING_COMPLETE, 'true');
  },

  // Complete cleanup
  async clearAll(): Promise<void> {
    const keys = Object.values(STORAGE_KEYS);
    await AsyncStorage.multiRemove(keys);
  },
};
Beveiliging van gevoelige gegevens

Voor gevoelige gegevens zoals authenticatietokens is het raadzaam expo-secure-store te gebruiken. Dit pakket versleutelt data via iOS Keychain en Android Keystore.

Responsive Styles and Theming

Een professionele applicatie moet zich aanpassen aan verschillende schermformaten en ondersteuning bieden voor de donkere modus.

theme/index.tstsx
import { Dimensions, PixelRatio, Platform } from 'react-native';

const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');

// Reference dimensions (iPhone 14)
const guidelineBaseWidth = 390;
const guidelineBaseHeight = 844;

// Scaling functions
export const scale = (size: number) =>
  (SCREEN_WIDTH / guidelineBaseWidth) * size;

export const verticalScale = (size: number) =>
  (SCREEN_HEIGHT / guidelineBaseHeight) * size;

export const moderateScale = (size: number, factor = 0.5) =>
  size + (scale(size) - size) * factor;

// Light theme
export const lightTheme = {
  colors: {
    primary: '#007AFF',
    secondary: '#5856D6',
    success: '#34C759',
    warning: '#FF9500',
    error: '#FF3B30',
    background: '#FFFFFF',
    surface: '#F2F2F7',
    text: '#000000',
    textSecondary: '#8E8E93',
    border: '#E5E5EA',
  },
  spacing: {
    xs: scale(4),
    sm: scale(8),
    md: scale(16),
    lg: scale(24),
    xl: scale(32),
  },
  typography: {
    h1: {
      fontSize: moderateScale(32),
      fontWeight: 'bold' as const,
      lineHeight: moderateScale(40),
    },
    h2: {
      fontSize: moderateScale(24),
      fontWeight: 'bold' as const,
      lineHeight: moderateScale(32),
    },
    body: {
      fontSize: moderateScale(16),
      lineHeight: moderateScale(24),
    },
    caption: {
      fontSize: moderateScale(14),
      lineHeight: moderateScale(20),
    },
  },
  borderRadius: {
    sm: scale(4),
    md: scale(8),
    lg: scale(12),
    full: 9999,
  },
};

// Dark theme
export const darkTheme = {
  ...lightTheme,
  colors: {
    ...lightTheme.colors,
    background: '#000000',
    surface: '#1C1C1E',
    text: '#FFFFFF',
    textSecondary: '#8E8E93',
    border: '#38383A',
  },
};

export type Theme = typeof lightTheme;
context/ThemeContext.tsxtsx
import React, { createContext, useContext, useState, useEffect } from 'react';
import { useColorScheme } from 'react-native';
import { lightTheme, darkTheme, Theme } from '../theme';
import { storage } from '../services/storage';

interface ThemeContextType {
  theme: Theme;
  isDark: boolean;
  toggleTheme: () => void;
  setThemeMode: (mode: 'light' | 'dark' | 'system') => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const systemColorScheme = useColorScheme();
  const [themeMode, setThemeMode] = useState<'light' | 'dark' | 'system'>('system');

  // Determine effective theme
  const isDark =
    themeMode === 'system'
      ? systemColorScheme === 'dark'
      : themeMode === 'dark';

  const theme = isDark ? darkTheme : lightTheme;

  // Load preferences on startup
  useEffect(() => {
    storage.getPreferences().then((prefs) => {
      if (prefs?.theme) {
        setThemeMode(prefs.theme);
      }
    });
  }, []);

  // Toggle between light and dark
  const toggleTheme = () => {
    const newMode = isDark ? 'light' : 'dark';
    setThemeMode(newMode);
    storage.getPreferences().then((prefs) => {
      storage.setPreferences({
        ...prefs,
        theme: newMode,
        notifications: prefs?.notifications ?? true,
        language: prefs?.language ?? 'en',
      });
    });
  };

  return (
    <ThemeContext.Provider value={{ theme, isDark, toggleTheme, setThemeMode }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

Preparing for Publication

Voor het publiceren van een app zijn verschillende configuratie- en optimalisatiestappen nodig.

app.jsonjson
{
  "expo": {
    "name": "MyApp",
    "slug": "my-app",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "automatic",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#007AFF"
    },
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "com.example.myapp",
      "buildNumber": "1"
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#007AFF"
      },
      "package": "com.example.myapp",
      "versionCode": 1
    },
    "plugins": [
      "expo-router"
    ]
  }
}
bash
# build.sh
# EAS Build configuration (Expo Application Services)
npm install -g eas-cli

# Log in to Expo account
eas login

# Configure the project
eas build:configure

# Build for iOS (simulator)
eas build --platform ios --profile development

# Build for Android (test APK)
eas build --platform android --profile preview

# Production build
eas build --platform all --profile production

# Store submission
eas submit --platform ios
eas submit --platform android
Certificaten en sleutels

Voor publicatie op iOS is een Apple Developer-account vereist (99 $/jaar) en voor Android een Google Play Console-account (eenmalig 25 $). EAS beheert ondertekeningscertificaten automatisch.

Conclusion

React Native biedt een efficiente aanpak voor de ontwikkeling van cross-platform mobiele apps. De basisprincipes uit deze gids stellen ontwikkelaars in staat om volledige en professionele applicaties te bouwen.

Checklist voor een geslaagde React Native-app

  • De ontwikkelomgeving opzetten met Expo voor een snelle start
  • Kerncomponenten beheersen: View, Text, TouchableOpacity, FlatList
  • Navigatie implementeren met React Navigation en TypeScript-typering
  • State beheren met de Context API en custom hooks
  • API-aanroepen structureren met een dedicated servicelaag
  • Gegevens lokaal persisteren met AsyncStorage
  • Een responsief themasysteem opzetten met ondersteuning voor de donkere modus

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

De officiele documentatie van React Native en Expo blijft het referentiepunt om elk onderwerp te verdiepen. Het ecosysteem evolueert snel en bibliotheken als React Query of Zustand kunnen het databeheer in complexere applicaties verder vereenvoudigen.

Tags

#react native
#mobile development
#javascript
#ios
#android

Delen

Gerelateerde artikelen