Розробка Додатків React Native у 2026: Повний Посібник та Питання для Співбесід

Опануйте розробку додатків React Native у 2026 році з Новою Архітектурою, JSI, Fabric та TurboModules. Практичний туторіал з прикладами коду та питаннями для технічних співбесід.

Розробка Додатків React Native у 2026: Повний Посібник та Питання для Співбесід

Розробка додатків React Native у 2026 році зазнала фундаментальних змін з виходом версії 0.87, яка впроваджує Нову Архітектуру як єдиний підтримуваний варіант. Старий bridge, що серіалізував кожен виклик JavaScript-до-native у JSON, повністю видалено та замінено на JSI (JavaScript Interface), який забезпечує пряму синхронну комунікацію.

Що змінилося в React Native 0.87

React Native 0.87 робить Нову Архітектуру обов'язковою. Старий bridge повністю видалено. Рендеринг складних списків працює на 43% швидше, просідання кадрів при прокрутці зменшилося на 95%, а використання пам'яті знизилося на 33% у бенчмарк-тестах.

Розуміння JSI та Нової Архітектури

JSI (JavaScript Interface) замінює асинхронний bridge прямими C++ зв'язками між JavaScript та нативним кодом. Замість серіалізації даних у JSON, передачі їх через чергу повідомлень та десеріалізації на іншому боці, JSI дозволяє JavaScript зберігати посилання на нативні об'єкти та викликати їхні методи напряму.

Ця архітектурна зміна уможливлює три ключові можливості: синхронні нативні виклики, спільне володіння об'єктами між шарами JavaScript та native, а також ліниве завантаження нативних модулів.

NativeUserModule.tstypescript
import { TurboModuleRegistry, TurboModule } from 'react-native';

export interface Spec extends TurboModule {
  getUserProfile(userId: string): Promise<{
    id: string;
    name: string;
    email: string;
  }>;
  
  // Синхронний виклик - можливий лише з JSI
  getDeviceLocale(): string;
}

export default TurboModuleRegistry.getEnforcing<Spec>('UserModule');

Метод getDeviceLocale() повертає результат миттєво без очікування Promise. Це було неможливо зі старим bridge, де кожен нативний виклик вимагав асинхронної серіалізації.

Створення Продакшн-Готового Компонента з Fabric

Fabric — це нова система рендерингу, що працює разом з JSI. Вона керує UI-деревом у C++ та синхронізується безпосередньо з JavaScript-потоком, усуваючи "зависання UI", що виникали коли старий bridge відставав під час швидких оновлень.

ProductCard.tsxtypescript
import React, { memo, useCallback } from 'react';
import {
  View,
  Text,
  Image,
  Pressable,
  StyleSheet,
} from 'react-native';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

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

interface ProductCardProps {
  product: Product;
  onPress: (id: string) => void;
}

const AnimatedPressable = Animated.createAnimatedComponent(Pressable);

export const ProductCard = memo(function ProductCard({
  product,
  onPress,
}: ProductCardProps) {
  const scale = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  const handlePressIn = useCallback(() => {
    scale.value = withSpring(0.95);
  }, [scale]);

  const handlePressOut = useCallback(() => {
    scale.value = withSpring(1);
  }, [scale]);

  const handlePress = useCallback(() => {
    onPress(product.id);
  }, [onPress, product.id]);

  return (
    <AnimatedPressable
      style={[styles.card, animatedStyle]}
      onPressIn={handlePressIn}
      onPressOut={handlePressOut}
      onPress={handlePress}
      accessibilityRole="button"
      accessibilityLabel={`${product.name}, ${product.price} гривень`}
    >
      <Image
        source={{ uri: product.imageUrl }}
        style={styles.image}
        resizeMode="cover"
      />
      <View style={styles.content}>
        <Text style={styles.name} numberOfLines={2}>
          {product.name}
        </Text>
        <Text style={styles.price}>{product.price.toFixed(2)} грн</Text>
        {!product.inStock && (
          <Text style={styles.outOfStock}>Немає в наявності</Text>
        )}
      </View>
    </AnimatedPressable>
  );
});

const styles = StyleSheet.create({
  card: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    overflow: 'hidden',
    marginHorizontal: 8,
    marginVertical: 4,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  image: {
    width: '100%',
    height: 160,
  },
  content: {
    padding: 12,
  },
  name: {
    fontSize: 16,
    fontWeight: '600',
    color: '#1a1a1a',
    marginBottom: 4,
  },
  price: {
    fontSize: 18,
    fontWeight: '700',
    color: '#2563eb',
  },
  outOfStock: {
    fontSize: 12,
    color: '#dc2626',
    marginTop: 4,
  },
});

Цей компонент використовує react-native-reanimated, який запускає анімації в UI-потоці через JSI. Хуки useSharedValue та useAnimatedStyle комунікують безпосередньо з нативним кодом без проходження через bridge.

Керування Станом з TanStack Query

Серверний стан у додатках React Native виграє від спеціалізованих інструментів. TanStack Query обробляє кешування, фонове оновлення та оптимістичні оновлення без необхідності глобального контейнера стану.

hooks/useProducts.tstypescript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

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

const API_BASE = 'https://api.example.com';

async function fetchProducts(category?: string): Promise<Product[]> {
  const url = category
    ? `${API_BASE}/products?category=${category}`
    : `${API_BASE}/products`;
  
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error('Не вдалося отримати товари');
  }
  return response.json();
}

export function useProducts(category?: string) {
  return useQuery({
    queryKey: ['products', category],
    queryFn: () => fetchProducts(category),
    staleTime: 5 * 60 * 1000, // 5 хвилин
    gcTime: 30 * 60 * 1000, // 30 хвилин
  });
}

export function useToggleStock() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (productId: string) => {
      const response = await fetch(`${API_BASE}/products/${productId}/toggle-stock`, {
        method: 'POST',
      });
      if (!response.ok) {
        throw new Error('Не вдалося оновити статус наявності');
      }
      return response.json();
    },
    // Оптимістичне оновлення
    onMutate: async (productId) => {
      await queryClient.cancelQueries({ queryKey: ['products'] });
      
      const previousProducts = queryClient.getQueryData<Product[]>(['products']);
      
      queryClient.setQueryData<Product[]>(['products'], (old) =>
        old?.map((p) =>
          p.id === productId ? { ...p, inStock: !p.inStock } : p
        )
      );
      
      return { previousProducts };
    },
    onError: (_err, _productId, context) => {
      if (context?.previousProducts) {
        queryClient.setQueryData(['products'], context.previousProducts);
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });
}

Callback onMutate виконує оптимістичне оновлення до відповіді сервера. Якщо мутація не вдається, onError відновлює попередній стан. Цей патерн забезпечує миттєвий зворотний зв'язок UI при збереженні консистентності даних.

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

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

Навігація з Expo Router

Expo Router забезпечує файлову маршрутизацію для React Native, подібну до Next.js App Router. Файлова структура автоматично визначає маршрути.

app/(tabs)/_layout.tsxtypescript
import { Tabs } from 'expo-router';
import { Home, Search, ShoppingCart, User } from 'lucide-react-native';

export default function TabsLayout() {
  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: '#2563eb',
        tabBarInactiveTintColor: '#6b7280',
        headerShown: false,
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: 'Головна',
          tabBarIcon: ({ color, size }) => (
            <Home color={color} size={size} />
          ),
        }}
      />
      <Tabs.Screen
        name="search"
        options={{
          title: 'Пошук',
          tabBarIcon: ({ color, size }) => (
            <Search color={color} size={size} />
          ),
        }}
      />
      <Tabs.Screen
        name="cart"
        options={{
          title: 'Кошик',
          tabBarIcon: ({ color, size }) => (
            <ShoppingCart color={color} size={size} />
          ),
        }}
      />
      <Tabs.Screen
        name="profile"
        options={{
          title: 'Профіль',
          tabBarIcon: ({ color, size }) => (
            <User color={color} size={size} />
          ),
        }}
      />
    </Tabs>
  );
}

Динамічні маршрути використовують нотацію квадратних дужок. Файл у app/product/[id].tsx обробляє будь-який шлях /product/123:

app/product/[id].tsxtypescript
import { useLocalSearchParams } from 'expo-router';
import { View, Text, ActivityIndicator } from 'react-native';
import { useQuery } from '@tanstack/react-query';

export default function ProductDetailScreen() {
  const { id } = useLocalSearchParams<{ id: string }>();

  const { data: product, isLoading, error } = useQuery({
    queryKey: ['product', id],
    queryFn: () => fetchProduct(id),
    enabled: !!id,
  });

  if (isLoading) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <ActivityIndicator size="large" color="#2563eb" />
      </View>
    );
  }

  if (error || !product) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Товар не знайдено</Text>
      </View>
    );
  }

  return (
    <View style={{ flex: 1, padding: 16 }}>
      <Text style={{ fontSize: 24, fontWeight: 'bold' }}>{product.name}</Text>
      <Text style={{ fontSize: 20, color: '#2563eb' }}>
        {product.price.toFixed(2)} грн
      </Text>
    </View>
  );
}

Поширені Питання на Співбесідах про Архітектуру React Native

Технічні співбесіди на позиції React Native у 2026 році значною мірою фокусуються на Новій Архітектурі. Наведені питання зустрічаються часто.

Фокусні Області на Співбесідах

Інтерв'юери очікують, що кандидати зможуть пояснити JSI, Fabric та TurboModules на концептуальному рівні. Запам'ятовування синтаксису менш важливе, ніж розуміння того, чому архітектура змінилася та які проблеми вона вирішує.

Питання: Що таке JSI і чому React Native його прийняв?

JSI (JavaScript Interface) — це C++ шар, який дозволяє JavaScript зберігати посилання на нативні об'єкти та безпосередньо викликати їхні методи. Старий bridge серіалізував кожен виклик у JSON, ставив його в асинхронну чергу та десеріалізував на нативній стороні. Це додавало затримки та унеможливлювало синхронні операції.

З JSI функція JavaScript може викликати нативний метод та отримати результат у тому самому кадрі. Це уможливлює такі функції, як синхронні виміри layout та пряма маніпуляція нативними view.

Питання: Чим Fabric відрізняється від старого рендерера?

Fabric — це система рендерингу на C++, яка підтримує UI-дерево в нативному коді замість JavaScript. Старий рендерер зберігав shadow tree в JavaScript та надсилав пакети оновлень через bridge.

Переваги Fabric:

  • Синхронні обчислення layout через JSI
  • Підтримка concurrent rendering (функції React 18 працюють коректно)
  • Краще керування пам'яттю зі спільним володінням між JS та native
  • Зменшений overhead серіалізації

Питання: Що таке TurboModules?

TurboModules — це заміна Native Modules. Ключові відмінності:

  1. Ліниве завантаження: TurboModules завантажуються лише при першому доступі, скорочуючи час запуску
  2. Типобезпека: CodeGen генерує типобезпечні зв'язки зі специфікацій TypeScript
  3. Синхронні методи: TurboModules можуть надавати синхронні функції за потреби
  4. Прямий доступ до JSI: Без серіалізації через bridge

Питання: Коли використовувати синхронний нативний метод?

Синхронні нативні методи підходять для операцій, що:

  • Завершуються за мікросекунди (читання locale пристрою, перевірка статусу дозволів)
  • Навмисно блокують UI (модальне підтвердження перед навігацією)
  • Повертають значення, потрібні для негайного рендерингу

Слід уникати синхронних методів для:

  • Мережевих запитів
  • Файлових I/O операцій окрім невеликих читань
  • Запитів до бази даних
  • Будь-якої операції, що перевищує 16мс (блокує кадр)

Більше питань для співбесід про React Native можна знайти в модулі networking та API та секції нативних модулів.

Тестування Компонентів React Native

React Native Testing Library надає утиліти для тестування компонентів так, як користувачі з ними взаємодіють, фокусуючись на доступності та поведінці замість деталей імплементації.

__tests__/ProductCard.test.tsxtypescript
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react-native';
import { ProductCard } from '../components/ProductCard';

const mockProduct = {
  id: 'prod-1',
  name: 'Бездротові Навушники',
  price: 149.99,
  imageUrl: 'https://example.com/headphones.jpg',
  inStock: true,
};

describe('ProductCard', () => {
  it('відображає інформацію про товар', () => {
    const onPress = jest.fn();
    render(<ProductCard product={mockProduct} onPress={onPress} />);

    expect(screen.getByText('Бездротові Навушники')).toBeOnTheScreen();
    expect(screen.getByText('149.99 грн')).toBeOnTheScreen();
  });

  it('викликає onPress з id товару при натисканні', () => {
    const onPress = jest.fn();
    render(<ProductCard product={mockProduct} onPress={onPress} />);

    fireEvent.press(
      screen.getByRole('button', { name: /бездротові навушники/i })
    );

    expect(onPress).toHaveBeenCalledWith('prod-1');
    expect(onPress).toHaveBeenCalledTimes(1);
  });

  it('показує повідомлення про відсутність коли товар недоступний', () => {
    const outOfStockProduct = { ...mockProduct, inStock: false };
    render(<ProductCard product={outOfStockProduct} onPress={jest.fn()} />);

    expect(screen.getByText('Немає в наявності')).toBeOnTheScreen();
  });
});

Тести робять запити за текстовим вмістом та ролями доступності замість test ID або внутрішніх деталей компонента. Цей підхід виявляє регресії, що впливають на користувачів, залишаючись стійким до рефакторингу.

Моніторинг Продуктивності в Продакшені

React Native 0.87 інтегрується з Sentry та подібними інструментами для моніторингу продуктивності в продакшені. Ключові метрики для відстеження:

  • Time to Interactive (TTI): Скільки часу до моменту, коли додаток реагує на введення користувача
  • Просідання кадрів: Кадри, що рендеряться довше 16.67мс
  • Використання JavaScript-потоку: Високе використання вказує на затратні обчислення, що блокують UI
  • Зростання пам'яті: Постійне зростання вказує на витоки
app/_layout.tsxtypescript
import * as Sentry from '@sentry/react-native';
import { useEffect } from 'react';
import { AppState, AppStateStatus } from 'react-native';

Sentry.init({
  dsn: process.env.EXPO_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.2,
  profilesSampleRate: 0.1,
  enableAutoPerformanceTracing: true,
});

export default function RootLayout() {
  useEffect(() => {
    const subscription = AppState.addEventListener(
      'change',
      (state: AppStateStatus) => {
        if (state === 'active') {
          Sentry.addBreadcrumb({
            category: 'app.lifecycle',
            message: 'Додаток став активним',
            level: 'info',
          });
        }
      }
    );

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

  // ... решта layout
}

Ключові Висновки для Розробки React Native у 2026

  • Нова Архітектура (JSI, Fabric, TurboModules) є обов'язковою в React Native 0.87. Старий bridge більше не існує.
  • JSI уможливлює синхронні нативні виклики. Використовувати їх слід обережно для операцій до 16мс.
  • Fabric рендерить UI у C++ та синхронізується з JavaScript через JSI. Це усуває асинхронне вузьке місце рендерингу.
  • TurboModules завантажуються ліниво та надають типобезпечні зв'язки, згенеровані зі специфікацій TypeScript.
  • Expo Router пропонує файлову навігацію, подібну до Next.js App Router.
  • React Native Testing Library тестує компоненти через їхній публічний API та дерево доступності.
  • Продакшн-додатки повинні відстежувати TTI, просідання кадрів та використання пам'яті з першого дня.

Для глибшого погляду на створення повноцінних додатків, див. туторіал повного додатку React Native. Посібник з Нової Архітектури охоплює міграцію зі старих версій.

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

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

Щоденний виклик

Чи знайдеш ти помилку в React Native?

Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Anthony Fillion-Maillet

Автор:

Anthony Fillion-Maillet

Засновник SharpSkill

Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.

Оновлено 6 вересня 2026 р.

Поділитися

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