Какие есть способы оптимизации производительности?

Оптимизация производительности в React Native играет критически важную роль при создании мобильных приложений, особенно когда речь идёт о сложных интерфейсах, больших объёмах данных и анимациях. Поскольку React Native работает в двух разных средах — JavaScript и нативной — необходимо учитывать оба уровня, чтобы обеспечить плавную, быструю и отзывчивую работу приложения. Существует множество способов улучшить производительность, начиная с оптимизации рендера компонентов и заканчивая контролем над работой bridge и использованием инструментов платформы.

1. Оптимизация рендеринга компонентов

Использование React.memo

Позволяет избежать повторного рендера функциональных компонентов, если их props не изменились.

const MyComponent = React.memo(({ title }) => {
return <Text>{title}</Text>;
});

shouldComponentUpdate и PureComponent

Для классовых компонентов можно переопределить shouldComponentUpdate, чтобы контролировать обновления.

class MyComponent extends React.PureComponent {
render() {
return <Text>{this.props.name}</Text>;
}
}

PureComponent делает поверхностное сравнение props и state и автоматически предотвращает лишние рендеры.

2. Мемоизация значений и функций

useMemo

Кэширует вычисления, чтобы они не выполнялись заново при каждом рендере.

const result = useMemo(() => expensiveCalculation(data), [data]);

useCallback

Кэширует функции, чтобы не создавать новую ссылку при каждом рендере.

const handlePress = useCallback(() => {
// ...
}, \[dependency\]);

3. Использование списков эффективно

FlatList вместо ScrollView для длинных списков

ScrollView отрисовывает все дочерние компоненты сразу, тогда как FlatList использует виртуализацию — отображает только видимые элементы, загружая остальные по мере прокрутки.

<FlatList
data={myData}
renderItem={({ item }) => &lt;Text&gt;{item.name}&lt;/Text&gt;}
keyExtractor={item => item.id}
/>

Настройка initialNumToRender, windowSize, maxToRenderPerBatch

Позволяет точно управлять количеством элементов, загружаемых в память:

<FlatList
initialNumToRender={10}
windowSize={5}
maxToRenderPerBatch={8}
/>

4. Оптимизация изображений

  • Используйте изображения в нужном разрешении (избегайте оригиналов 4K для иконок)

  • Предзагружайте изображения с Image.prefetch(url)

  • Используйте resizeMode и cache:

<Image
source={{ uri: '...', cache: 'force-cache' }}
resizeMode="contain"
/>
  • Используйте библиотеки для оптимизации загрузки, например, react-native-fast-image

5. Lazy loading экранов и компонентов

Для больших экранов или модулей применяйте отложенную загрузку:

<Stack.Screen
name="Settings"
getComponent={() => require('./screens/Settings').default}
/>

Или:

const LazyComponent = React.lazy(() => import('./MyHeavyComponent'));

6. Избегание ненужных ререндеров

  • Следите за тем, чтобы компоненты не перерисовывались при каждом изменении родителя.

  • Не передавайте анонимные функции и новые объекты/массивы в props без необходимости:

// Плохо:
&lt;MyComponent data={\[\]} onPress={() =&gt; {}} />
// Лучше:
const data = useMemo(() => \[...\], \[\]);
const onPress = useCallback(() => {...}, \[\]);

7. Использование InteractionManager

Позволяет отложить выполнение тяжёлых операций до завершения всех анимаций и взаимодействий:

import { InteractionManager } from 'react-native';
useEffect(() => {
const task = InteractionManager.runAfterInteractions(() => {
// тяжёлые вычисления
});
return () => task.cancel();
}, \[\]);

8. Выгрузка неиспользуемых экранов и компонентов

В React Navigation можно размонтировать экран при уходе с него:

<Stack.Screen
name="Profile"
component={ProfileScreen}
options={{ unmountOnBlur: true }}
/>

Это освобождает память и снижает нагрузку.

9. Управление памятью

  • Очищайте подписки, таймеры и слушатели в useEffect

  • Не забывайте clearInterval, clearTimeout, removeListener

  • Используйте WeakRef или WeakMap, если необходимо

10. Анимации с использованием Reanimated или NativeDriver

React Native по умолчанию использует JavaScript-драйвер для анимаций, который может быть лагучим.

Рекомендуется использовать native driver:

Animated.timing(this.state.value, {
toValue: 1,
useNativeDriver: true
}).start();

Либо использовать более производительную библиотеку:

11. Использование useFocusEffect вместо useEffect

Если компонент должен выполнять действия при фокусе экрана, лучше использовать useFocusEffect из @react-navigation/native:

import { useFocusEffect } from '@react-navigation/native';
useFocusEffect(
useCallback(() => {
// Выполнить при заходе на экран
return () => {
// Очистка при выходе с экрана
};
}, \[\])
);

Это предотвращает ненужные эффекты, когда экран не в фокусе.

12. Включение Hermes

Hermes — это JavaScript-движок, разработанный Facebook специально для React Native. Он:

  • Ускоряет запуск приложения

  • Уменьшает потребление памяти

  • Поддерживает JSI (новая архитектура)

Чтобы включить Hermes:

Для Android:
В android/app/build.gradle:

project.ext.react = \[
enableHermes: true
\]

13. Профилирование и отладка

Используйте инструменты анализа производительности:

  • Flipper с плагинами React DevTools и Performance Monitor

  • Встроенные DevMenu и React Native Inspector

  • Android Studio и Xcode Instruments

  • why-did-you-render — отслеживает лишние ререндеры

npm install @welldone-software/why-did-you-render
if (\__DEV_\_) {
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React);
}

14. Разбиение больших компонентов

  • Разделяйте крупные экраны на мелкие переиспользуемые компоненты

  • Не загромождайте один компонент множеством логик и состояний

  • Выносите тяжёлые элементы (например, сложные списки, изображения) в отложенные компоненты

15. Использование дебаунса и троттлинга

Для предотвращения перегрузки UI/сети при частых событиях (например, скролле, вводе текста):

import debounce from 'lodash.debounce';
const handleSearch = debounce((text) => {
// отправка запроса
}, 300);

Оптимизация React Native-приложения требует комплексного подхода: от снижения количества перерисовок компонентов до контроля загрузки данных и использования нативных инструментов. Регулярное профилирование и анализ производительности позволяют находить узкие места и устранять их до того, как они начнут влиять на пользовательский опыт.