React Native - основы

19 июня 2026
Автор

Олег Марков

Введение

React Native — это фреймворк для создания кроссплатформенных мобильных приложений под iOS и Android с использованием JavaScript и React. В отличие от гибридных решений на основе WebView, React Native отображает настоящие нативные UI-компоненты, что обеспечивает высокую производительность и привычный пользовательский опыт.

В этой статье мы разберём фундаментальные концепции React Native: базовые компоненты, принципы стилизации, управление состоянием, обработку пользовательского ввода и работу с данными. После изучения вы будете готовы создавать полноценные мобильные приложения.

Если вы хотите освоить React Native глубоко и комплексно — приходите на наш курс React Native и Expo Router. На курсе 184 урока и 11 упражнений, AI-тренажеры для безлимитной практики с кодом и задачами 24/7, живое ревью наставника и еженедельные встречи с менторами.

Архитектура React Native

Прежде чем писать код, важно понять, как React Native работает под капотом.

Потоки выполнения

React Native использует многопоточную архитектуру:

  • JS Thread — выполняет ваш JavaScript-код: обрабатывает логику приложения, управляет состоянием, вызывает обработчики событий
  • UI Thread (Main Thread) — нативный поток для рендеринга компонентов и обработки жестов
  • Shadow Thread — вычисляет layout с помощью Yoga (кросс-платформенная реализация Flexbox)

Новая архитектура (JSI)

В современных версиях React Native (0.68+) введена новая архитектура на базе JSI (JavaScript Interface):

  • Заменяет старый асинхронный Bridge на синхронный интерфейс
  • Fabric — новый рендерер, улучшающий производительность UI
  • TurboModules — более эффективная работа с нативными модулями
  • CodeGen — автоматическая генерация кода для нативных модулей
// Пример: старый Bridge (асинхронный)
// JS → Bridge → Native (задержка из-за сериализации JSON)

// Новая архитектура JSI (синхронный доступ)
// JS → JSI → Native (прямое взаимодействие через C++)

Базовые компоненты

React Native предоставляет набор базовых компонентов, которые компилируются в нативные элементы интерфейса.

View

View — основной строительный блок, аналог <div> в HTML. Используется для группировки и позиционирования элементов.

import React from 'react';
import { View, StyleSheet } from 'react-native';

function CardComponent() {
  return (
    <View style={styles.card}>
      <View style={styles.header} />
      <View style={styles.body} />
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    backgroundColor: '#fff',
    borderRadius: 8,
    padding: 16,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    elevation: 3, // только для Android
  },
  header: {
    height: 48,
    backgroundColor: '#6200ee',
    borderRadius: 4,
  },
  body: {
    marginTop: 12,
    height: 100,
  },
});

Text

Весь текст в React Native обязательно должен быть обёрнут в компонент Text. Нельзя размещать строки прямо в View.

import React from 'react';
import { Text, View } from 'react-native';

function TypographyExample() {
  return (
    <View>
      {/* Базовый текст */}
      <Text>Простой текст</Text>

      {/* Стилизованный текст */}
      <Text style={{ fontSize: 24, fontWeight: 'bold', color: '#333' }}>
        Заголовок
      </Text>

      {/* Вложенный текст — наследует стили родителя */}
      <Text style={{ color: 'blue' }}>
        Текст синим, а <Text style={{ fontWeight: 'bold' }}>этот жирный и синий</Text>
      </Text>

      {/* Ограничение строк */}
      <Text numberOfLines={2} ellipsizeMode="tail">
        Очень длинный текст, который будет обрезан после второй строки с многоточием в конце
      </Text>
    </View>
  );
}

Image

Компонент для отображения изображений из сети, локального файла или bundle.

import React from 'react';
import { Image, View, StyleSheet } from 'react-native';

function ImageExample() {
  return (
    <View>
      {/* Изображение из сети */}
      <Image
        source={{ uri: 'https://example.com/photo.jpg' }}
        style={styles.networkImage}
        resizeMode="cover"
        onLoad={() => console.log('Загружено!')}
        onError={(e) => console.log('Ошибка:', e.nativeEvent.error)}
      />

      {/* Локальное изображение */}
      <Image
        source={require('./assets/logo.png')}
        style={styles.localImage}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  networkImage: {
    width: '100%',
    height: 200,
    borderRadius: 8,
  },
  localImage: {
    width: 120,
    height: 120,
  },
});

TextInput

Компонент для пользовательского ввода текста.

import React, { useState } from 'react';
import { TextInput, View, Text, StyleSheet } from 'react-native';

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  return (
    <View style={styles.form}>
      <TextInput
        style={styles.input}
        placeholder="Email"
        placeholderTextColor="#999"
        value={email}
        onChangeText={setEmail}
        keyboardType="email-address"
        autoCapitalize="none"
        autoCorrect={false}
      />

      <TextInput
        style={styles.input}
        placeholder="Пароль"
        placeholderTextColor="#999"
        value={password}
        onChangeText={setPassword}
        secureTextEntry={true}
        returnKeyType="done"
      />

      <Text style={styles.hint}>
        Введите email: {email}
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  form: {
    padding: 16,
  },
  input: {
    height: 48,
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    paddingHorizontal: 16,
    marginBottom: 12,
    fontSize: 16,
    color: '#333',
  },
  hint: {
    color: '#666',
    fontSize: 14,
  },
});

TouchableOpacity и Pressable

Для обработки нажатий на элементы используются специальные обёртки.

import React from 'react';
import { TouchableOpacity, Pressable, Text, View, StyleSheet } from 'react-native';

function ButtonsExample() {
  return (
    <View style={styles.container}>
      {/* TouchableOpacity — снижает прозрачность при нажатии */}
      <TouchableOpacity
        style={styles.button}
        onPress={() => console.log('Нажали!')}
        onLongPress={() => console.log('Долгое нажатие!')}
        activeOpacity={0.7}
      >
        <Text style={styles.buttonText}>TouchableOpacity</Text>
      </TouchableOpacity>

      {/* Pressable — более гибкий, современный вариант */}
      <Pressable
        style={({ pressed }) => [
          styles.button,
          pressed && styles.buttonPressed,
        ]}
        onPress={() => console.log('Pressable нажали!')}
      >
        {({ pressed }) => (
          <Text style={styles.buttonText}>
            {pressed ? 'Нажато!' : 'Pressable'}
          </Text>
        )}
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    gap: 12,
    padding: 16,
  },
  button: {
    backgroundColor: '#6200ee',
    paddingVertical: 12,
    paddingHorizontal: 24,
    borderRadius: 8,
    alignItems: 'center',
  },
  buttonPressed: {
    backgroundColor: '#3700b3',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
});

ScrollView и FlatList

Для прокручиваемого контента в React Native используют ScrollView или FlatList.

import React from 'react';
import { ScrollView, FlatList, View, Text, StyleSheet } from 'react-native';

// ScrollView — для небольшого количества элементов
function ScrollExample() {
  return (
    <ScrollView
      showsVerticalScrollIndicator={false}
      contentContainerStyle={styles.scrollContent}
    >
      {Array.from({ length: 20 }, (_, i) => (
        <View key={i} style={styles.item}>
          <Text>Элемент {i + 1}</Text>
        </View>
      ))}
    </ScrollView>
  );
}

// FlatList — для длинных списков с виртуализацией
const DATA = Array.from({ length: 1000 }, (_, i) => ({
  id: String(i),
  title: `Элемент ${i + 1}`,
}));

function FlatListExample() {
  const renderItem = ({ item }: { item: typeof DATA[0] }) => (
    <View style={styles.item}>
      <Text>{item.title}</Text>
    </View>
  );

  return (
    <FlatList
      data={DATA}
      keyExtractor={(item) => item.id}
      renderItem={renderItem}
      initialNumToRender={10}
      maxToRenderPerBatch={10}
      windowSize={5}
      ItemSeparatorComponent={() => <View style={styles.separator} />}
      ListEmptyComponent={<Text>Список пуст</Text>}
      ListHeaderComponent={<Text style={styles.header}>Список элементов</Text>}
    />
  );
}

const styles = StyleSheet.create({
  scrollContent: {
    padding: 16,
  },
  item: {
    padding: 16,
    backgroundColor: '#f5f5f5',
    borderRadius: 8,
    marginBottom: 8,
  },
  separator: {
    height: 8,
  },
  header: {
    fontSize: 20,
    fontWeight: 'bold',
    padding: 16,
  },
});

Стилизация в React Native

React Native использует подмножество CSS с некоторыми особенностями.

StyleSheet.create

Всегда используйте StyleSheet.create вместо inline-объектов — это повышает производительность и позволяет React Native оптимизировать стили.

import { StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  // Все свойства в camelCase (не kebab-case)
  container: {
    flex: 1,
    backgroundColor: '#f0f0f0',
    paddingHorizontal: 16,  // paddingLeft + paddingRight
    paddingVertical: 8,     // paddingTop + paddingBottom
  },
  text: {
    fontSize: 16,
    lineHeight: 24,
    color: '#333333',
    fontWeight: '500',      // строка, не число
    textAlign: 'center',
  },
  // Числа — пиксели по умолчанию (НЕ rem или px)
  box: {
    width: 100,
    height: 100,
    borderRadius: 8,
  },
});

Flexbox

React Native использует Flexbox для верстки. В отличие от веб, здесь flexDirection по умолчанию column, а alignContentflex-start.

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

function FlexboxExample() {
  return (
    <View style={styles.container}>
      {/* Горизонтальное расположение */}
      <View style={styles.row}>
        <View style={[styles.box, { backgroundColor: '#e91e63' }]}>
          <Text style={styles.boxText}>1</Text>
        </View>
        <View style={[styles.box, { backgroundColor: '#9c27b0', flex: 1 }]}>
          <Text style={styles.boxText}>2 (flex: 1)</Text>
        </View>
        <View style={[styles.box, { backgroundColor: '#2196f3' }]}>
          <Text style={styles.boxText}>3</Text>
        </View>
      </View>

      {/* Центрирование */}
      <View style={styles.centered}>
        <Text>По центру</Text>
      </View>

      {/* Space between */}
      <View style={styles.spaceBetween}>
        <Text>Слева</Text>
        <Text>В центре</Text>
        <Text>Справа</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    gap: 16,
  },
  row: {
    flexDirection: 'row',
    gap: 8,
  },
  box: {
    width: 60,
    height: 60,
    borderRadius: 8,
    justifyContent: 'center',
    alignItems: 'center',
  },
  boxText: {
    color: '#fff',
    fontWeight: 'bold',
  },
  centered: {
    height: 80,
    backgroundColor: '#e8f5e9',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 8,
  },
  spaceBetween: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    backgroundColor: '#fff3e0',
    padding: 16,
    borderRadius: 8,
  },
});

Адаптивные размеры

import { Dimensions, PixelRatio, Platform } from 'react-native';

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

// Функция для адаптивных размеров
const scale = (size: number) => (SCREEN_WIDTH / 375) * size;

const styles = StyleSheet.create({
  // Процентные значения (работают для width/height)
  halfWidth: {
    width: '50%',
  },
  // Адаптивный размер шрифта
  adaptiveText: {
    fontSize: scale(16),
  },
  // Платформо-специфичные стили
  platformBox: {
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 2 },
        shadowOpacity: 0.15,
        shadowRadius: 4,
      },
      android: {
        elevation: 4,
      },
    }),
  },
});

Управление состоянием

React Native использует те же хуки React, что и веб-разработка.

useState и useEffect

import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator, FlatList } from 'react-native';

interface Post {
  id: number;
  title: string;
  body: string;
}

function PostsScreen() {
  const [posts, setPosts] = useState<Post[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let isMounted = true;

    async function fetchPosts() {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
        if (!response.ok) throw new Error('Ошибка загрузки');
        const data = await response.json();
        if (isMounted) {
          setPosts(data);
        }
      } catch (err) {
        if (isMounted) {
          setError(err instanceof Error ? err.message : 'Неизвестная ошибка');
        }
      } finally {
        if (isMounted) {
          setLoading(false);
        }
      }
    }

    fetchPosts();

    // Очистка при размонтировании
    return () => {
      isMounted = false;
    };
  }, []);

  if (loading) {
    return <ActivityIndicator size="large" color="#6200ee" />;
  }

  if (error) {
    return <Text style={{ color: 'red', padding: 16 }}>{error}</Text>;
  }

  return (
    <FlatList
      data={posts}
      keyExtractor={(item) => String(item.id)}
      renderItem={({ item }) => (
        <View style={{ padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' }}>
          <Text style={{ fontWeight: 'bold', marginBottom: 4 }}>{item.title}</Text>
          <Text style={{ color: '#666' }}>{item.body}</Text>
        </View>
      )}
    />
  );
}

useCallback и useMemo

import React, { useState, useCallback, useMemo } from 'react';
import { View, TextInput, FlatList, Text } from 'react-native';

const ITEMS = Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  name: `Товар ${i + 1}`,
  price: Math.floor(Math.random() * 10000),
}));

function SearchableList() {
  const [query, setQuery] = useState('');

  // useMemo — кэшируем фильтрацию
  const filteredItems = useMemo(
    () => ITEMS.filter((item) =>
      item.name.toLowerCase().includes(query.toLowerCase())
    ),
    [query]
  );

  // useCallback — стабильный рендер-метод для FlatList
  const renderItem = useCallback(
    ({ item }: { item: typeof ITEMS[0] }) => (
      <View style={{ padding: 12, borderBottomWidth: 1, borderBottomColor: '#eee' }}>
        <Text>{item.name}</Text>
        <Text style={{ color: '#999' }}>{item.price} ₽</Text>
      </View>
    ),
    []
  );

  return (
    <View style={{ flex: 1 }}>
      <TextInput
        style={{ height: 44, borderWidth: 1, borderColor: '#ddd', margin: 16, paddingHorizontal: 12, borderRadius: 8 }}
        placeholder="Поиск..."
        value={query}
        onChangeText={setQuery}
      />
      <FlatList
        data={filteredItems}
        keyExtractor={(item) => String(item.id)}
        renderItem={renderItem}
      />
    </View>
  );
}

Context API

import React, { createContext, useContext, useState, ReactNode } from 'react';

// Типы для контекста
interface User {
  id: number;
  name: string;
  email: string;
}

interface AuthContextType {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
  isAuthenticated: boolean;
}

// Создаём контекст
const AuthContext = createContext<AuthContextType | null>(null);

// Провайдер
export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);

  const login = async (email: string, password: string) => {
    // Симуляция запроса к API
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
      headers: { 'Content-Type': 'application/json' },
    });
    const data = await response.json();
    setUser(data.user);
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider
      value={{ user, login, logout, isAuthenticated: !!user }}
    >
      {children}
    </AuthContext.Provider>
  );
}

// Хук для использования контекста
export function useAuth(): AuthContextType {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth должен использоваться внутри AuthProvider');
  }
  return context;
}

// Использование в компоненте
function ProfileScreen() {
  const { user, logout, isAuthenticated } = useAuth();

  if (!isAuthenticated) {
    return <Text>Войдите в систему</Text>;
  }

  return (
    <View>
      <Text>Привет, {user?.name}!</Text>
      <TouchableOpacity onPress={logout}>
        <Text>Выйти</Text>
      </TouchableOpacity>
    </View>
  );
}

Платформенные особенности

Platform API

import { Platform, StyleSheet } from 'react-native';

// Определение платформы
const isIOS = Platform.OS === 'ios';
const isAndroid = Platform.OS === 'android';

// Версия платформы
const iosVersion = Platform.Version; // string: "17.0"
const androidVersion = Platform.Version; // number: 33

// Выбор значений по платформе
const statusBarHeight = Platform.select({
  ios: 44,
  android: 24,
  default: 0,
});

const styles = StyleSheet.create({
  header: {
    paddingTop: Platform.select({ ios: 50, android: 24 }),
    backgroundColor: '#6200ee',
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 1 },
        shadowOpacity: 0.2,
        shadowRadius: 2,
      },
      android: {
        elevation: 4,
      },
    }),
  },
});

SafeAreaView

На современных устройствах есть "вырезы" (notch), динамический остров и нижние индикаторы. SafeAreaView автоматически добавляет отступы.

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

function App() {
  return (
    <SafeAreaView style={styles.safeArea}>
      <StatusBar
        barStyle="dark-content"
        backgroundColor="#fff"
        translucent={false}
      />
      <View style={styles.content}>
        <Text style={styles.title}>Мое приложение</Text>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  safeArea: {
    flex: 1,
    backgroundColor: '#fff',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
  },
});

Работа с данными: AsyncStorage

Для хранения простых данных на устройстве используется AsyncStorage.

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

// Сохранение данных
async function saveUserData(user: object) {
  try {
    await AsyncStorage.setItem('user', JSON.stringify(user));
    console.log('Данные сохранены');
  } catch (error) {
    console.error('Ошибка сохранения:', error);
  }
}

// Чтение данных
async function loadUserData() {
  try {
    const jsonValue = await AsyncStorage.getItem('user');
    if (jsonValue !== null) {
      return JSON.parse(jsonValue);
    }
    return null;
  } catch (error) {
    console.error('Ошибка чтения:', error);
    return null;
  }
}

// Удаление данных
async function clearUserData() {
  try {
    await AsyncStorage.removeItem('user');
  } catch (error) {
    console.error('Ошибка удаления:', error);
  }
}

// Использование с useEffect
function useUserData() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    loadUserData().then(setUser);
  }, []);

  const saveUser = useCallback(async (userData: object) => {
    await saveUserData(userData);
    setUser(userData);
  }, []);

  return { user, saveUser };
}

Навигация: React Navigation

React Navigation — стандартный инструмент для навигации в React Native.

npm install @react-navigation/native @react-navigation/stack
npx expo install react-native-screens react-native-safe-area-context
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { View, Text, Button } from 'react-native';

// Типизация параметров навигации
type RootStackParamList = {
  Home: undefined;
  Detail: { id: number; title: string };
  Profile: { userId: number };
};

const Stack = createStackNavigator<RootStackParamList>();

// Экраны
function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text style={{ fontSize: 24, marginBottom: 16 }}>Главная</Text>
      <Button
        title="Перейти к деталям"
        onPress={() => navigation.navigate('Detail', { id: 1, title: 'Статья 1' })}
      />
    </View>
  );
}

function DetailScreen({ route, navigation }) {
  const { id, title } = route.params;

  return (
    <View style={{ flex: 1, padding: 16 }}>
      <Text style={{ fontSize: 20, fontWeight: 'bold' }}>{title}</Text>
      <Text style={{ color: '#666', marginTop: 8 }}>ID: {id}</Text>
      <Button title="Назад" onPress={() => navigation.goBack()} />
    </View>
  );
}

// Настройка навигатора
function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Home"
        screenOptions={{
          headerStyle: { backgroundColor: '#6200ee' },
          headerTintColor: '#fff',
          headerTitleStyle: { fontWeight: 'bold' },
        }}
      >
        <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Главная' }} />
        <Stack.Screen
          name="Detail"
          component={DetailScreen}
          options={({ route }) => ({ title: route.params.title })}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Работа с сетью (fetch и axios)

fetch API

import React, { useState, useEffect } from 'react';

interface ApiError {
  message: string;
  status: number;
}

async function apiRequest<T>(url: string, options?: RequestInit): Promise<T> {
  const response = await fetch(url, {
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      ...options?.headers,
    },
    ...options,
  });

  if (!response.ok) {
    const error: ApiError = {
      message: `HTTP Error: ${response.status}`,
      status: response.status,
    };
    throw error;
  }

  return response.json();
}

// Создание записи
async function createPost(title: string, body: string) {
  const post = await apiRequest<{ id: number }>('https://jsonplaceholder.typicode.com/posts', {
    method: 'POST',
    body: JSON.stringify({ title, body, userId: 1 }),
  });
  return post;
}

Кастомный хук для запросов

import { useState, useEffect, useCallback } from 'react';

interface UseApiState<T> {
  data: T | null;
  loading: boolean;
  error: string | null;
  refetch: () => void;
}

function useApi<T>(url: string): UseApiState<T> {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [trigger, setTrigger] = useState(0);

  useEffect(() => {
    let cancelled = false;

    async function load() {
      setLoading(true);
      setError(null);
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const result = await response.json();
        if (!cancelled) setData(result);
      } catch (err) {
        if (!cancelled) {
          setError(err instanceof Error ? err.message : 'Ошибка запроса');
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    }

    load();
    return () => { cancelled = true; };
  }, [url, trigger]);

  const refetch = useCallback(() => setTrigger((t) => t + 1), []);

  return { data, loading, error, refetch };
}

// Использование
function UserProfile({ userId }: { userId: number }) {
  const { data: user, loading, error, refetch } = useApi<{ name: string; email: string }>(
    `https://jsonplaceholder.typicode.com/users/${userId}`
  );

  if (loading) return <ActivityIndicator />;
  if (error) return (
    <View>
      <Text>Ошибка: {error}</Text>
      <Button title="Повторить" onPress={refetch} />
    </View>
  );

  return (
    <View>
      <Text>{user?.name}</Text>
      <Text>{user?.email}</Text>
    </View>
  );
}

Жизненный цикл и хуки

AppState

import React, { useEffect, useRef } from 'react';
import { AppState, AppStateStatus } from 'react-native';

function useAppState(
  onChange?: (state: AppStateStatus) => void
) {
  const appState = useRef(AppState.currentState);

  useEffect(() => {
    const subscription = AppState.addEventListener('change', (nextAppState) => {
      if (
        appState.current.match(/inactive|background/) &&
        nextAppState === 'active'
      ) {
        console.log('Приложение на переднем плане');
        // Например: обновить данные, проверить уведомления
      }

      appState.current = nextAppState;
      onChange?.(nextAppState);
    });

    return () => subscription.remove();
  }, [onChange]);

  return appState.current;
}

Keyboard API

import React, { useEffect, useState } from 'react';
import { Keyboard, KeyboardEvent } from 'react-native';

function useKeyboard() {
  const [keyboardHeight, setKeyboardHeight] = useState(0);
  const [keyboardVisible, setKeyboardVisible] = useState(false);

  useEffect(() => {
    const showSubscription = Keyboard.addListener(
      'keyboardDidShow',
      (e: KeyboardEvent) => {
        setKeyboardHeight(e.endCoordinates.height);
        setKeyboardVisible(true);
      }
    );

    const hideSubscription = Keyboard.addListener(
      'keyboardDidHide',
      () => {
        setKeyboardHeight(0);
        setKeyboardVisible(false);
      }
    );

    return () => {
      showSubscription.remove();
      hideSubscription.remove();
    };
  }, []);

  return { keyboardHeight, keyboardVisible };
}

Нативные возможности

Vibration

import { Vibration, Platform } from 'react-native';

function hapticFeedback() {
  if (Platform.OS === 'android') {
    // Android: вибрация 200мс
    Vibration.vibrate(200);
  } else {
    // iOS: используйте @react-native-community/haptic-feedback для богатых вибраций
    Vibration.vibrate();
  }
}

Linking

import { Linking, Alert } from 'react-native';

async function openWebsite(url: string) {
  const canOpen = await Linking.canOpenURL(url);
  if (canOpen) {
    await Linking.openURL(url);
  } else {
    Alert.alert('Ошибка', `Не удалось открыть: ${url}`);
  }
}

// Открыть телефон
function callPhone(phone: string) {
  Linking.openURL(`tel:${phone}`);
}

// Открыть email
function sendEmail(email: string) {
  Linking.openURL(`mailto:${email}?subject=Привет`);
}

// Открыть карты
function openMaps(lat: number, lng: number) {
  const url = Platform.select({
    ios: `maps:${lat},${lng}`,
    android: `geo:${lat},${lng}`,
  });
  if (url) Linking.openURL(url);
}

Отладка и инструменты

Встроенные инструменты отладки

// console.log для отладки
console.log('Данные:', JSON.stringify(data, null, 2));
console.warn('Предупреждение');
console.error('Ошибка');

// __DEV__ — только в development режиме
if (__DEV__) {
  console.log('Это только для разработки');
}

Flipper

Flipper — официальный дебаггер для React Native. Позволяет:

  • Инспектировать элементы (React DevTools)
  • Смотреть сетевые запросы
  • Просматривать базу данных SQLite
  • Отслеживать производительность

React DevTools

# Установка
npm install -g react-devtools

# Запуск
react-devtools

Лучшие практики

Структура проекта

src/
├── components/        # Переиспользуемые компоненты
│   ├── ui/            # Базовые UI: Button, Input, Card
│   └── features/      # Фичевые компоненты
├── screens/           # Экраны приложения
├── navigation/        # Настройка навигации
├── hooks/             # Кастомные хуки
├── services/          # API-вызовы, бизнес-логика
├── store/             # Глобальное состояние (Redux/Zustand)
├── utils/             # Вспомогательные функции
├── constants/         # Константы, конфиг
└── types/             # TypeScript типы

Оптимизация производительности

import React, { memo, useCallback } from 'react';

// memo — не перерисовывать без изменения props
const ListItem = memo(({ item, onPress }: { item: Item; onPress: (id: number) => void }) => {
  return (
    <Pressable onPress={() => onPress(item.id)}>
      <Text>{item.title}</Text>
    </Pressable>
  );
});

// Стабильный колбэк для передачи в дочерние компоненты
function List({ items }: { items: Item[] }) {
  const handlePress = useCallback((id: number) => {
    console.log('Нажали на', id);
  }, []);

  return (
    <FlatList
      data={items}
      keyExtractor={(item) => String(item.id)}
      renderItem={({ item }) => <ListItem item={item} onPress={handlePress} />}
      removeClippedSubviews={true}  // Удалять невидимые элементы из памяти
      getItemLayout={(data, index) => ({  // Если известна высота — ускоряет прокрутку
        length: 64,
        offset: 64 * index,
        index,
      })}
    />
  );
}

TypeScript в React Native

import React from 'react';
import { View, Text, StyleProp, ViewStyle, TextStyle } from 'react-native';

// Интерфейс компонента
interface ButtonProps {
  title: string;
  onPress: () => void;
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  style?: StyleProp<ViewStyle>;
  textStyle?: StyleProp<TextStyle>;
  loading?: boolean;
}

const VARIANT_COLORS = {
  primary: '#6200ee',
  secondary: '#03dac6',
  danger: '#b00020',
};

const SIZE_PADDING = {
  small: { vertical: 6, horizontal: 12 },
  medium: { vertical: 10, horizontal: 20 },
  large: { vertical: 14, horizontal: 28 },
};

function Button({
  title,
  onPress,
  variant = 'primary',
  size = 'medium',
  disabled = false,
  style,
  textStyle,
  loading = false,
}: ButtonProps) {
  const padding = SIZE_PADDING[size];

  return (
    <Pressable
      onPress={onPress}
      disabled={disabled || loading}
      style={[
        {
          backgroundColor: disabled ? '#ccc' : VARIANT_COLORS[variant],
          paddingVertical: padding.vertical,
          paddingHorizontal: padding.horizontal,
          borderRadius: 8,
          alignItems: 'center',
          opacity: disabled ? 0.6 : 1,
        },
        style,
      ]}
    >
      {loading ? (
        <ActivityIndicator color="#fff" size="small" />
      ) : (
        <Text
          style={[{ color: '#fff', fontWeight: '600', fontSize: 16 }, textStyle]}
        >
          {title}
        </Text>
      )}
    </Pressable>
  );
}

Заключение

Мы рассмотрели основные концепции React Native:

  • Архитектура: JS Thread, UI Thread, новая архитектура JSI
  • Базовые компоненты: View, Text, Image, TextInput, TouchableOpacity, Pressable, ScrollView, FlatList
  • Стилизация: StyleSheet.create, Flexbox, адаптивные размеры, Platform.select
  • Управление состоянием: useState, useEffect, useCallback, useMemo, Context API
  • Навигация: React Navigation, Stack Navigator
  • Работа с данными: fetch API, AsyncStorage, кастомные хуки
  • Нативные возможности: AppState, Keyboard, Vibration, Linking
  • Оптимизация: memo, FlatList-оптимизации, TypeScript-типизация

React Native предоставляет богатую экосистему для создания полноценных мобильных приложений. Понимание этих основ является фундаментом для дальнейшего изучения более продвинутых тем: Expo Router, анимации с Reanimated, нативных модулей и публикации приложений.

Если вы хотите стать профессиональным разработчиком React Native — присоединяйтесь к нашему курсу React Native и Expo Router. Вас ждут 184 урока, практические задания с AI-тренажерами и живое ревью от опытных наставников.

Стрелочка влевоГайд по использованию React Native CLIНастройка Android Studio на React NativeСтрелочка вправо

Постройте личный план изучения React-native до уровня Middle — бесплатно!

React-native — часть карты развития Mobile

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по React-native

Стили в React Native
Стили в React NativeНавигация в React Native (react-navigation)Как использовать векторные иконки в React Native5 популярных библиотек UI компонентов React NativeСоздание и использование tabs в React NativeОптимизация переходов между экранами в React NativeАдаптация safe area context на React NativeОбзор библиотек для навигации в React NativeСоздание сложных анимаций (reanimated) на React NativeИспользование библиотеки стилей Paper в React NativeРуководство по стилизации компонентов в React NativeРуководство по navigation в React NativeОптимизация отображения списков в React NativeКак реализовать linking на React NativeГайд по UI-китам для React NativeГде искать elements для приложения на React NativeРабота с цветами в React Native
Работа с компонентом view в React NativeРабота с компонентом text в React NativeС чего начать разработку проекта на React NativeУстановка и настройка React Native SDKНастройка React Native для работы с iOSПошаговая инструкция по установке React NativeЧто такое React Native Expo и зачем он нуженГайд по использованию React Native CLIReact Native - основыНастройка Android Studio на React NativeНастройка React Native для работы с AndroidЧто такое React Native Введение для начинающихНастройка и использование плагина Gradle на React NativeExpo — платформа для React Native разработки
Работа со ScrollView в React NativeОсновные компоненты React NativeРабота с видео в React NativeКак реализовать аудиоплеер на React NativeНастройка и использование input и textinput в React NativeИнтеграция видео плеера в приложение на React NativeИспользование выпадающих списков в React NativeСоздание и настройка native module на React NativeКак создать модальные окна в React NativeРабота с изображениями в React NativeОтображение списков данных в React NativeГайд по файловой системе в React NativeИнтеграция камеры в приложение на React NativeСоздание интерактивных кнопок в React NativeНативные модули в React Native
Открыть базу знаний

Лучшие курсы по теме

изображение курса

React Native и Expo Router

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Основы JavaScript

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий