React Native: Rozrobka povnotsinnoho mobilnoho zastosunku u 2026 rotsi

Vycherpnyi posibnyk zi stvorennia mobilnykh zastosunkiv dlia iOS ta Android za dopomohoiu React Native. Vid nalashtuvannia seredovyshcha do publikatsii -- vse neobkhidne dlia startu.

Rozrobka mobilnykh zastosunkiv z React Native

React Native daje zmoghu stvoryuvaty natyvni mobilni zastosunky dlia iOS ta Android z yedynoi kodovoyi bazy na JavaScript. Freimvork rozrobliaietsya Meta i vykorystovuietsya v zastosunkakh Instagram, Facebook ta Discord. Vin zabezpechuie shvydku rozrobku bez vtraty natyvnoi produktyvnosti.

Why React Native in 2026?

Nova arkhitektura (Fabric i TurboModules) vzhe stabilna, tomu React Native proponuie produktyvnist, blyzku do natyvnoi, zberigaiuchy pry tsomu efektyvnist veb-rozrobky. Ponad 40% z 500 naipopuliarnishykh zastosunkiv vykorystovuiut React Native.

Development Environment Setup

Pered pochatkom roboty neobkhidno vstanovyty potribni instrumenty. React Native proponuie dva pidkhody: Expo (rekomendovanyi dlia pochatkivtsiv) ta React Native CLI (dlia bilshoho kontroliu).

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 znachno sproshchuie rozrobku, avtomatychno obrobliaiiuchy natyvnu konfiguratsiiu. Dlia testuvannia zastosunku dostatno vstanovyty Expo Go na smartfon i skanuvaty QR-kod, shcho vidobrazhuietsia na ekrani.

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 nadaie natyvni komponenty, shcho zaminiuiut HTML-elementy. Kozhen komponent bezposeredno peretvoriuietsia na natyvni komponenty iOS abo Android.

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 by Default

React Native za zamovchuvanniiam vykorystovuie Flexbox z flexDirection: 'column' (na vidminu vid vebu, de za zamovchuvanniiam row). Tsia riznytsya vazhlyva pid chas proektuvannia maketiv.

Handling User Interactions

Dotykovni vzaiemodii obrobliaiiutsia za dopomohoiu spetsializovanykh komponentiv. Kozhen typ vzaiemodii maie vlasnyyi komponent, optymizovanyi dlia produktyvnosti.

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

Navihatsiya -- tse neviddiemnyi element bud-yakoho mobilnoho zastosunku. React Navigation ye standartnym rishenniam, shcho proponuie rizni typy navihatsii, prystosovani do mobilnykh pativ.

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',
  },
});

Готовий до співбесід з React Native?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

State Management with Context and Hooks

Dlia prostoho ta serednoho rivnia keruvannia stanom React Context u poiednanni z khukami ye efektyvnym rishenniam bez zovnishnikh zalezhnostei.

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',
  },
});
Global vs Local State

Dlia skladnishykh zastosunkiv z rozhornytym spilnym stanom krashche pidkhodyat rishennia na kshtalt Zustand abo Redux Toolkit. Context optymalnyi dlia stanu, shcho zminiuietsia nechasto (tema, avtentyfikatsiya).

API Calls and Data Management

Zviazok iz bekendom ye tsentralnym elementom bud-yakoho mobilnoho zastosunku. Navedenii nizhche pidkhid vykorystovuie sharo abstrakcii dlia vyklykiv API.

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

Lokalne zberihannia danykh dozvoliaie zberihaty informatsiiu mizh sesiiamy. AsyncStorage -- standartne rishennia dlia prostykh danykh, a SQLite pidkhodyt dlia skladnykh strukturovanykh danykh.

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);
  },
};
Sensitive Data Security

Dlia zberihannia sensytyvnykh danykh, takykh yak tokeny avtentyfikatsii, varto vykorystovuvaty expo-secure-store, yakyi shyfruie dani cherez iOS Keychain ta Android Keystore.

Responsive Styles and Theming

Profesiinyi zastosunok povynen adaptuvuvatysia do riznykh rozmiriv ekraniv ta pidtrymuvaty temnyi rezhym.

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

Pered publikatsiieiu zastosunku neobkhidno vykonaty kilka krokiv konfihuratsii ta optymizatsii.

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
Certificates and Keys

Dlia publikatsii v iOS neobkhidnyi oblikovyi zapys Apple Developer (99 USD/rik), a dlia Android -- oblikovyi zapys Google Play Console (odnorazova oplata 25 USD). EAS avtomatychno keruie sertyfikatamy pidpysu.

Conclusion

React Native proponuie efektyvnyi pidkhid do rozrobky krosplatformennykh mobilnykh zastosunkiv. Osnovy, rozhliianuti v tsomu posibnyku, dozvoliaiut stvoriuvaty povnotsinni ta profesiini zastosunky.

Checklist for a Successful React Native App

  • Nalashtuvannia seredovyshcha z Expo dlia shvydkoho startu
  • Opanuvannia bazovykh komponentiv: View, Text, TouchableOpacity, FlatList
  • Realizatsiya navihatsii z React Navigation ta typizatsiieiu TypeScript
  • Keruvannia stanom za dopomohoiu Context API ta vlasnykh khukiv
  • Strukturuvannia vyklykiv API z vydilenoi servisnoi shary
  • Lokalne zberihannia danykh za dopomohoiu AsyncStorage
  • Stvorennia adaptyvnoi systemy tem z pidtrymkoiu temnoho rezhymu

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Ofitsiina dokumentatsiya React Native ta Expo zalyshaietsya osnovnym dzherelom dlia pohlyblennia kozhnoi temy. Ekosystema shvydko rozvyvaietsya, i biblioteky na kshtalt React Query chy Zustand mozhut shche bilshe sproshchyty keruvannia danymy u skladnishykh zastosunkakh.

Теги

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

Поділитися

Пов'язані статті