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

Розробка додатків React Native у 2026 році зазнала фундаментальних змін з виходом версії 0.87, яка впроваджує Нову Архітектуру як єдиний підтримуваний варіант. Старий bridge, що серіалізував кожен виклик JavaScript-до-native у JSON, повністю видалено та замінено на JSI (JavaScript Interface), який забезпечує пряму синхронну комунікацію.
React Native 0.87 робить Нову Архітектуру обов'язковою. Старий bridge повністю видалено. Рендеринг складних списків працює на 43% швидше, просідання кадрів при прокрутці зменшилося на 95%, а використання пам'яті знизилося на 33% у бенчмарк-тестах.
Розуміння JSI та Нової Архітектури
JSI (JavaScript Interface) замінює асинхронний bridge прямими C++ зв'язками між JavaScript та нативним кодом. Замість серіалізації даних у JSON, передачі їх через чергу повідомлень та десеріалізації на іншому боці, JSI дозволяє JavaScript зберігати посилання на нативні об'єкти та викликати їхні методи напряму.
Ця архітектурна зміна уможливлює три ключові можливості: синхронні нативні виклики, спільне володіння об'єктами між шарами JavaScript та native, а також ліниве завантаження нативних модулів.
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 відставав під час швидких оновлень.
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 обробляє кешування, фонове оновлення та оптимістичні оновлення без необхідності глобального контейнера стану.
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. Файлова структура автоматично визначає маршрути.
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:
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. Ключові відмінності:
- Ліниве завантаження: TurboModules завантажуються лише при першому доступі, скорочуючи час запуску
- Типобезпека: CodeGen генерує типобезпечні зв'язки зі специфікацій TypeScript
- Синхронні методи: TurboModules можуть надавати синхронні функції за потреби
- Прямий доступ до JSI: Без серіалізації через bridge
Питання: Коли використовувати синхронний нативний метод?
Синхронні нативні методи підходять для операцій, що:
- Завершуються за мікросекунди (читання locale пристрою, перевірка статусу дозволів)
- Навмисно блокують UI (модальне підтвердження перед навігацією)
- Повертають значення, потрібні для негайного рендерингу
Слід уникати синхронних методів для:
- Мережевих запитів
- Файлових I/O операцій окрім невеликих читань
- Запитів до бази даних
- Будь-якої операції, що перевищує 16мс (блокує кадр)
Більше питань для співбесід про React Native можна знайти в модулі networking та API та секції нативних модулів.
Тестування Компонентів React Native
React Native Testing Library надає утиліти для тестування компонентів так, як користувачі з ними взаємодіють, фокусуючись на доступності та поведінці замість деталей імплементації.
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
- Зростання пам'яті: Постійне зростання вказує на витоки
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Засновник SharpSkill
Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.
Оновлено 6 вересня 2026 р.
Поділитися
Пов'язані статті

Посібник з розробки додатків React Native 2026: Продакшн-застосунки та питання для співбесіди
Повний посібник з розробки продакшн-застосунків React Native у 2026 році, що охоплює Expo SDK 56, EAS Build, Hermes V1, Нову Архітектуру та підготовку до співбесід для розробників React Native.

React Native 0.87 та SwiftPM у 2026: Сучасний iOS Build та Питання для Співбесід
React Native 0.87 впроваджує експериментальну підтримку Swift Package Manager, усуваючи потребу в CocoaPods. Дізнайтеся про налаштування SwiftPM, автолінкування та підготовку до співбесід.

Flutter vs React Native Продуктивність 2026: Бенчмарки та Питання для Співбесід
Порівняння продуктивності Flutter vs React Native у 2026 році. Бенчмарки Impeller vs New Architecture, використання пам'яті, FPS та питання для технічних співбесід.