Идея этой статьи родилась во время задушевных разговоров на кухне и за утренним кофе с коллегами. Началось все с вопроса: «Что ты сказал бы себе прошлому, чтобы релизы стали легче, а спринты прекраснее?» Конечно, в тот момент я ничего дельного не ответил, но, кажется, наконец-то сформулировал идеальную фразу: «Настоящий разработчик, Слав, это не тот, кто с сияющим макбуком и вкусным смузи. Разработчик — тот, кто делает то, что нужно сделать. И в легаси покопаться приходится, и memory leak дебажить, а он все равно делает, потому что так надо».

Привет, Хабр! Меня зовут Вячеслав Чащухин, я — разработчик в МойОфис. Занимаюсь мобильной версией Squadus — цифрового рабочего пространства для совместной работы и деловых коммуникаций.

Обращаю ваше внимание на то, что эта статья — не инструкция по решению всех проблем. Скорее, чек-лист причин, которые, по моему мнению, чаще всего приводят к тем или иным проблемам. Поэтому в некоторых пунктах будут ссылки или упоминания возможных решений. Я объединил проблемы по группам. Если вы обнаружили в своем приложении подлагивания или не хотите их обнаружить в будущем, рекомендую пройтись по пунктам:)


Наше мобильное приложение Squadus разработано на фреймворке React Native – это прекрасный инструмент для создания кроссплатформенных приложений. Он базируется, как понятно из названия, на React, поэтому логично, что часть проблем, которые будут описаны актуальны и для ReactJS. Естественно таким мультитулом из мира мобильной разработки надо уметь правильно пользоваться. Поэтому я собрал часть самых популярных ошибок, которые уменьшают производительность вашего приложения.

В статье мы поговорим о:

  1. Избыточных ре-рендерах

  2. Оптимизации стилей

  3. Оптимизации bridge-коммуникации

  4. Эффективных списках

  5. Анимации без лагов

  6. Работе с изображениями

  7. Быстрой навигации

  8. Оптимизации дев-режима

  9. Вредных советах

1. Лишние ре-рендеры в компонентах

Первое и самое частое, из-за чего может страдать производительность приложения — частые обновления компонентов. Особенно если эти компоненты содержат в себе логику.

Как этого избежать — следите за следующими вещами

  • Обернуты ли дочерние элементы в React.memo для предотвращения лишних обновлений от обновлений родителя

Делайте это внимательно, так как не все компоненты должны быть в React.memo. Подробнее об этом в разделе вредных советов.

  • Избегаете ли вы создания новых объектов или функций в props

Это особенно критично в случае списков. Например, если в renderItem положить inline функции, ее пересоздание значительно уменьшит производительность. Можете использовать useMemo для объектов и useCallback для функций, чтобы они не пересоздавались.

Пример

<FlatList 
  data={messages}
  renderItem={({ item }) => ( 
    <MessageItem 
      message={item} 
      onPress={() => { 
          console.log('Pressed message', item.id); 
        }} 
    /> 
  )}
/>

  • Объявляете ли вы функции прямо в JSX

Пример

<Button title="Save" onPress={() => saveData()} /> – обработчик onPress определен как стрелочная функция в месте использования.

Почему плохо

При каждом ре-рендере создается новая функция, из-за чего дочерний компонент (Button в примере) будет считать prop onPress изменившимся и ре-рендериться заново. Это сводит на нет пользу от React.memo для потомков и в целом нагружает сборщик мусора из-за множества краткоживущих объектов-функций. В итоге интерфейс может перерисовываться чаще, чем нужно.

Как поступить

Определяйте функции-обработчики вне компонента или используйте useCallback внутри него. Это позволит передавать вниз один и тот же экземпляр функции и не вызывать лишних перерисовок.

Если функция callback не использует переменные или функции из контекста компонента, то выносите ее из компонента в util function. Так мы не будем каждый раз делать проверки внутри useCallback.

Например

const saveData = useCallback(() => { ... }, [deps]);
return <Button title="Save" onPress={saveData} />;

Такой подход предотвратит создание новых функций каждый раз и повысит шансы, что потомки не будут обновляться зря.

  • Обращайте внимание, когда useMemo и useCallback используются для простых вычислений

Само их использование требует ресурсов, поэтому накладные расходы на проверки зависимостей могут быть дороже пересоздания.

Пример

Создание функции, которая возвращает строку:

const simpleFunction = () => "Hello";

Применение useCallback:

const memoizedFunction = useCallback(() => "Hello", []);

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

  • Минимизируете ли вы обновления состояния

Убедитесь, что состояние обновляется только при необходимости. Используйте useReducer, если требуется сложная логика управления состоянием.

  • Разделяете ли состояние на локальное и глобальное

Избегайте хранения локального состояния в глобальных менеджерах (MobX, Jotai, Zustand), если оно не нужно другим частям приложения. Используйте локальные хуки для управления состоянием в небольших компонентах.

Пример проблем с ре-рендером

Внимательно следите за компонентами, которые получают данные и события от пользователя. В таких местах могут возникать лишние ре-рендеры, вредящие производительности и нарушающие работу компонента.

Такие действия можно обернуть в debounce. Так можно получить самые актуальные данные от пользователя и избежать дублирования запросов или других сайд эффектов.

<TextInput/>
import React, { useState } from 'react';
import { TextInput, StyleSheet } from 'react-native';

export const UselessTextInput = () => {
   const [value, setValue] = useState('Text');
   const onChangeText = (text) => {
     setValue(text);
   };
   return (
     <TextInput
       accessibilityLabel='Text input field'
       style={styles.textInput}
       onChangeText={onChangeText}
       value={value}
     />
   );
};

Здесь мы видим проблему избыточных ререндеров. При обновлении onChangeText обновится и состояние value, которое затем вновь обновит значение value — и так с каждым вводом. Также при быстром наборе вы столкнетесь с проблемой, при которой часть ввода будет теряться, так как обновления происходят асинхронно.

Пример ввода

Все было бы хорошо, если пользователь вводит текст достаточно медленно. Но что произойдет, если ввод будет молниеносным (или чуть быстрее обычного)? Например, пользователь вводит T → E → S → T, но E и S — почти одновременно. В событии, выделенным красным, можно увидеть, что после onChangeText("TE") не успело вызваться срабатывание setValue.

Чтобы это исправить, отвяжите обновление состояния от актуального значения текстового ввода. Нативный компонент сам прекрасно может отобразить то, что в нем указано. Используя defaultValue, мы сохраним значение, если произойдет ре-рендер и отвяжем нативный компонент от обновлений состояния.

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

export const BestTextInput = () => {
   const [value, setValue] = useState('Text');
   const onChangeText = (text) => {
     setValue(text);
   };

   return (
     <TextInput
       accessibilityLabel='Text input field'
       style={styles.textInput}
       onChangeText={onChangeText}
       defaultValue={value}
     />
     <Text>
       {value}
     </Text>
   );
};

2. Советы по оптимизации стилей

  • Выносите стили за пределы рендера

Определяйте стили с помощью StyleSheet.create или как константы вне функции компонента. Это предотвращает создание нового объекта стилей на каждый рендер и снижает нагрузку на JavaScript-тред.

Вот хороший пример того, как стоит использовать StyleSheet.create:

const createStyles = (theme: Theme) => {
  return StyleSheet.create({
    container: {
      alignItems: isIOS ? 'center' : 'flex-start',
      flexDirection: 'column',
    },
    subTitleText: {
      ...textStyles.D2,
      color: themes[theme].auxiliaryText,
    },
  });
};

Так мы не теряем возможность например переключать стили в зависимости от темы, но также сохраняем высокую производительность за счет использования StyleSheet. Рекомендуется обернуть такую функцию в useMemo, чтобы кешировать последний созданный стиль и менять только при изменении входных данных:

const styles = useMemo(() => createStyles(theme), [theme]);

  • Избегайте инлайн-стилей в JSX

Они приводят к дополнительным временным затратам и мешают повторному использованию кэшированных стилей (возможно увеличение времени запуска на ~300–400 мс при большом количестве элементов).

  • Помните, Yoga уже все оптимизировала

Под капотом React Native использует движок Yoga на нативной стороне для расчёта layout. Когда вы создаете стили через StyleSheet.create, React Native отправляет их на нативную сторону всего один раз, присваивая им уникальные ID. При последующих рендерах в bridge передаются только эти идентификаторы вместо громоздких объектов стилей. Если же каждый раз создавать новый объект стиля прямо в рендере, мост будет вынужден сериализовать и отправлять его при каждом обновлении UI, что увеличивает нагрузку.

  • StyleSheet vs CSS-in-JS библиотеки

Встроенный StyleSheet достаточно эффективен для большинства приложений. Библиотеки вроде styled-components или Emotion упрощают работу со стилями (CSS-в-среде JS, темизация и т.д.), но могут внести оверхед. В React Native styled-components, например, под капотом все равно вызывает StyleSheet.create для создания статических стилей, однако при динамических стилях (зависящих от props) эти библиотеки генерируют новые стили буквально на лету.

Это добавляет нагрузку на JS (расчет стилей, дополнительный компонент-обертка) и может сказаться на производительности, особенно при большом количестве компонентов. Emotion и подобные решения работают схожим образом. Если производительность критична, отдавайте предпочтение базовому StyleSheet и тщательно профилируйте использование сторонних решений.

  • Минимизируйте вред от стилей

По возможности переиспользуйте один и тот же объект стилей для одинаковых элементов, вместо создания дубликатов. Для изменяющихся стилей, например, цвет фона в зависимости от состояния, применяйте мемоизацию (useMemo), чтобы не вычислять стиль заново на каждом рендере без необходимости. Старайтесь не менять часто свойства layout’а (такие как размеры, отступы), иначе Yoga будет каждый раз пересчитывать раскладку интерфейса. Проще говоря, держите стилевую логику максимально простой и статичной – это позволит UI-треду работать плавно.

3. Оптимизация коммуникации через bridge

Традиционный мост React Native (между JS и native) может стать узким местом, если через него проходит слишком много данных. Каждый вызов по bridge требует сериализации данных в JSON и обратной десериализации на другой стороне, плюс контекст-переключение между потоками. Если ваш JavaScript-код шлет сотни событий в секунду или огромный объем данных, UI может подвисать.

  • Минимизируйте количество пересечений моста

Лучше отправить один батч данных, чем десятки мелких сообщений.

  • Используйте оптимальные структуры данных

Например, передавайте крупные наборы данных как одну структуру (массив, объект), а не по одному элементу.

  • Ограничивайте частоту событий

Для часто срабатывающих событий, таких как прокрутка, жесты, сенсоры, применяйте throttle/debounce, чтобы снизить нагрузку. Если нужно передать очень большой объем данных, например, изображение или результат тяжёлых вычислений, рассмотрите возможность выполнить работу на нативной стороне (через нативный модуль) или использование нового механизма JSI (для новой архитектуры), минуя старый мост. Чем меньше вы «дергаете» bridge, тем более отзывчивым будет приложение.

4. Чек-лист для списков

  • Проверьте, используете ли вы виртуализированные компоненты

Для длинных списков используйте FlatList или SectionList вместо ScrollView. При этом не забывайте настраивать параметрыinitialNumToRender, windowSize, и maxToRenderPerBatch. А для создания уникальных ключей для каждого элемента — используйте keyExtractor.

Пример

<FlatList
  data={data}
  renderItem={({ item }) => <Text>{item.name}</Text>}
  keyExtractor={(item) => item.id.toString()}
  initialNumToRender={10}
  windowSize={5}
  maxToRenderPerBatch={10}
/>

Совет

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

getItemLayout={(data, index) => ({
  length: ITEM_HEIGHT,
  offset: ITEM_HEIGHT * index,
  index,
})}

Для больших списков (более 100), особенно с медиа-контентом, лучше использовать FlashList. Если же в вашем списке есть анимации, используйте react-native-reanimated.

  • Подгружаете ли вы данные порциями в большой список

Используйте onEndReached и настройте onEndReachedThreshold, чтобы подгружать данные в нужный момент.

Пример

<FlatList
  onEndReached={() => fetchMoreData()}
  onEndReachedThreshold={0.5} // Подгружает, когда прокручено 50%
 />
  • Кэшируите ли вы результаты для повторного использования

5. Чек-лист для анимации

  • Применяете ли вы нативные анимации

Используйте react-native-reanimated для нативно управляемых анимаций и минимизируйте анимации, управляемые на JavaScript-стороне.

  • Используйте useNativeDriver в Animated

Если нет возможности перейти на reanimated. Ограничения — подходит для свойств, которые могут быть анимированы нативно (например, transform, opacity).

  • Минимизируйте количество одновременно работающих анимаций

Одновременное выполнение большого числа анимаций создает нагрузку на CPU и GPU.

  • Старайтесь группировать элементы или применять общие анимации

Если анимация должна быть незаметной для пользователя, задумайтесь о её необходимости.

  • Используйте LayoutAnimation для простых переходов

Это встроенный инструмент React Native, который упрощает анимацию изменений в макете (например, добавление/удаление элементов).

Пример

import { LayoutAnimation, UIManager } from 'react-native';

    if (Platform.OS === 'android') {
      UIManager.setLayoutAnimationEnabledExperimental(true);
    }

    const handlePress = () => {
      LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
      setItems([...items, newItem]);
    };
  • Оптимизируйте использование компонентов Animated

Используйте Animated.createAnimatedComponent для оборачивания сложных компонентов, таких как FlatList, вместо создания анимаций напрямую.

Пример

import Animated from 'react-native-reanimated';
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
  • Используйте библиотеку react-native-gesture-handler для жестов

Она более производительна по сравнению со стандартными обработчиками. Особенно в комбинации с react-native-reanimated для создания плавных взаимодействий (например, свайпы, драги).

  • Избегайте анимаций вне видимого экрана

Они тратят ресурсы впустую. Используйте виртуализацию в сочетании с компонентами списка (FlatList, FlashList).

  • Применяйте debounce и throttle для жестов

Обработчики, вызывающие слишком частые обновления состояния, могут замедлить работу.

  • Используйте дебаунс или троттлинг для уменьшения частоты вызовов функций

Если у вас есть очень сложная анимация, в которой участвует множество элементов. Стоит воспользоваться Rive или Lottie, но лучше Rive:)

  • Если присутствуют сложные фигуры, то проще будет их реализовать с помощью react-native-skia

6. Совет для изображений

  • Используете ли вы оптимизированную библиотеку для изображений

Используйте react-native-fast-image или faster-image для более эффективных загрузки и кэширования. Проверяйте и используйте настройки кеширования библиотек. Не забывайте о возможностях сделать предзагрузку изображений, если это необходимо. И реализуйте ленивую загрузку изображений в длинных списках — так вы не будете тратить ресурсы, если пользователь не дойдет до этого элемента в списке.

7. Чек-лист ре-рендеров при навигации

  • Проверьте, обновляются ли компоненты при каждом заходе на экран

Большое количество ре-рендеров сразу после навигации могут сильно замедлять работу приложения. Также происходит, если компоненты перерисовывают, даже если экран не отрисовывается с нуля. Например, когда мы перешли дальше по Stack-навигации, а потом вернулись назад.

  • Изучите параметры для оптимизации в библиотеке, которую вы используете для навигации (чаще всего это React Navigation)

Убедитесь, что состояние навигации не вызывает лишних ре-рендеров. Это может происходить, если на одном из вложенных экранов есть подписка на состояние навигации, где не проверяется какой экран сейчас в фокусе. Используйте useFocusEffect.

И еще один совет — изучите подробнее тему ленивой загрузки экранов.

8. Как следить за производительностью во время разработки

Самый простой и быстрый способ – найти проблемы с ре-рендерами. В местах, которые являются важными узлами (экран с большим количеством дочерних компонентов, тяжелый компонент с большим количеством вычислений) – расставить console.count('ComponentName') – это позволит видеть в логах при разработке, что какой-то из экранов слишком много ре-рендерится.

На стадии разработки важно регулярно профилировать приложение, используя встроенные инструменты React Native, такие как React DevTools, Flipper, а также штатный Android Profiler или Xcode Instruments. Эти инструменты позволяют выявлять «узкие места» в производительности, например, лишние ре-рендеры компонентов, долгие вычисления и утечки памяти. Так использование React DevTools Profiler помогает анализировать, какие компоненты рендерятся чаще, чем нужно, что часто указывает на неправильное управление состоянием или мемоизацией.

Дополнительно можно интегрировать библиотеки для измерения производительности, такие как react-native-performance или react-native-performance-stats, чтобы получить детализированные метрики, включая время рендеринга, FPS и задержки в UI. Это особенно полезно при разработке списков или анимаций, где каждый миллисекундный лаг может негативно сказаться на UX.

Также рекомендую поставить why-did-you-render. Эта библиотека позволит вам видеть из-за чего компоненты ре-рендерятся. Ее удобно как использовать при поиске проблемы, так и поставить на компонент, в котором вы уверены, чтобы в случае появления проблем, разработчики увидели логи и устранили проблему как можно быстрее.

9. Вредные советы

Совет 1. Оборачивайте каждый компонент в React.memo для максимальной производительности

Пример неправильного применения

Обернуть все ваши компоненты в React.memo, вне зависимости от их задач: export default React.memo(MyComponent).

Почему плохо

Бездумное использование React.memo/PureComponent может усложнить код и даже ухудшить производительность.

Во-первых, мемоизация каждого компонента добавляет дополнительные проверки props на каждом рендере. Если props всегда новые (например, передаются новые объекты или функции), то толку от такой обертки нет – компонент все равно будет обновляться, но при этом React потратит время на сравнение props.

Во-вторых, можно столкнуться с ситуацией «почему UI не обновляется?», если забыть, что React.memo по умолчанию делает только поверхностное сравнение.

Правильная альтернатива

Используйте React.memo точечно для компонентов, которые действительно часто ре-рендерятся без изменения props. Сначала проведите профилирование и убедитесь, что узким местом являются лишние рендеры. Затем мемоизируйте компонент и убедитесь, что ему не передаются каждый раз новые объекты. Например, вынесите стили и обработчики событий вне рендера.

Правильно примененные PureComponent/React.memo позволяют избежать лишних обновлений UI, но применять их «на всякий случай» повсюду не следует. Также стоит учесть, что если у вас есть компонент, который имеет вложенность, то недостаточно мемоизировать верхний уровень. Мемоизиация в таком случае должна происходить также на уровне дочерних компонентов.

Совет 2. Загружайте все данные приложения сразу при запуске

Пример

При старте приложения выполнить сразу несколько крупных запросов (список товаров, профиль пользователя, ленту новостей) и загрузить пачку изображений в кэш, «чтобы потом не ждать».

Почему плохо

Инициализация приложения становится тяжелой и долгой. Пока загрузится весь объем данных, пользователь будет ждать с пустым экраном или сплэш-скрином. Более того, много параллельных операций могут блокировать JS-тред (который отвечает за отрисовку), вызывая ощущение подвисания. Часть загруженных ресурсов может вообще не понадобиться пользователю, если он не дойдет до соответствующих экранов. В этом случае вы потратите трафик, время и память впустую.

Правильная альтернатива

Загружайте данные лениво, по мере необходимости. Разбейте большие запросы на более мелкие: например, подгружайте контент следующей страницы списка по мере прокрутки, используя onEndReached у списков. Изображения в длинных списках загружайте лениво через placeholders или библиотеки, когда элемент списка появляется на экране.

То же касается и модулей — современные возможности позволяют выполнять динамический импорт экранов или функционала. Используйте React.lazy и Suspense для отложенной загрузки крупных экранов или фич, которые не необходимы сразу при старте. Это существенно снизит время запуска приложения и потребление ресурсов, пользователь увидит интерфейс быстрее и скорее всего не столкнется с фризами.

Совет 3. Для длинных списков вполне можно использовать ScrollView

Пример

Отобразить 500 элементов списка с помощью <ScrollView>{bigArray.map(...)</ScrollView>, вместо заморочек с FlatList.

Почему плохо

ScrollView без виртуализации пытается отрендерить все 500 элементов сразу. Это мгновенно съест много памяти и нагрузит JS-тред вычислением всех этих элементов, что приведёт к долгой прокрутке и, вероятно, к зависаниям или вылету приложения на слабых устройствах. Производительность списка резко упадет — кадры инт ерфейса могут пропускаться, а анимация прокрутки станет дерганой.

Правильная альтернатива

Для любых сколько-нибудь длинных списков используйте FlatList или SectionList, которые из коробки реализуют виртуализацию (рендерят только видимые элементы) и оптимизируют потребление памяти. У FlatList настройте параметры initialNumToRender, windowSize, maxToRenderPerBatch под ваши данные, и обязательно используйте keyExtractor для стабильных ключей элементов. Если размер каждого элемента известен, реализуйте getItemLayout – это ускорит расчет позиций.

Для сверхбольших списков (сотни элементов и сложный UI) присмотритесь к библиотекам типа FlashList — они дают лучшую производительность и плавность прокрутки. И помните про ленивую подгрузку данных страницами, чтобы не держать в памяти больше, чем нужно в данный момент.

Заключение

Оптимизация производительности React Native – крайне обширная тема, и мы рассмотрели лишь вершину айсберга. Основная идея, которая проходит через все советы — это осознанность. Профилируйте ваше приложение, чтобы найти узкие места: лишние рендеры, медленные операции или перегруженный мост. И уже зная проблему, применяйте подходящие оптимизации: выносите стили, мемоизируйте что нужно, загружайте данные по требованию, избегайте анти-паттернов. Такой сбалансированный подход позволит вашему React Native приложению быть быстрым и отзывчивым, сохранив при этом дружелюбность разработки и богатый функционал.

Какие аспекты оптимизации вам кажутся самыми сложными? Напишите в комментариях, о чем стоит рассказать подробнее в следующей статье — возможно, ваш вопрос станет темой нового материала.

И если после прочтения захотелось применить эти знания на практике — загляните в вакансии МойОфис. У нас есть задачи, где можно работать над крутыми фичами для миллионов пользователей.

Комментарии (16)


  1. Galy4a
    03.06.2025 15:00

    Такой подход предотвратит создание новых функций каждый раз и повысит шансы, что потомки не будут обновляться зря.

    Стрелочная функция передаваемая аргументом в useCallback как создавалась при каждом рендере так и будет создаваться. Неизменным будет только возвращаемое хуком значение, при условии, что зависимости не изменились.


    1. lear
      03.06.2025 15:00

      Поэтому можно использовать mobx и action.bound =)


    1. SalaarFiend Автор
      03.06.2025 15:00

      Это верное наблюдение, но важно различать создание функции и сохранение одной и той же ссылкиuseCallback и нужен именно для последнего — чтобы ссылка оставалась стабильной, пока не изменятся зависимости.


      1. Galy4a
        03.06.2025 15:00

        Именно это написано в моем комментарии и не совсем это написанно в цитируемом мной абзаце.


      1. BanderasPRO
        03.06.2025 15:00

        Да, важно различать. Но вы пишете "Такой подход предотвратит создание новых функций". И вам, как раз указывают на ошибку, что такой подход ВООБЩЕ НИКАК не предотвратить создание новых функций.

        useCallback(()=>{}, []) - тут создается новая стрелочная функция (аргумент для useCallback), дальше дело техники, относительно, результата, но именно тут ВООБЩЕ НЕТ никакой оптимизации, касающиеся вашего пункта "нагружает сборщик мусора из-за множества краткоживущих объектов-функций". Функции в вашем "подходе", как создавались, так и создаются, в том же количестве и с той же частотой.


        1. SalaarFiend Автор
          03.06.2025 15:00

          Вы и @Galy4a правы.
          Фраза про пересоздание функций не корректна, её стоило бы сформулировать как "передает всегда одну и ту же ссылку на функцию, если [deps] не менялся."
          В примере useCallback(()=>{}, []) смысла никакого нет, это подсвечено в блоке - Обращайте внимание, когда useMemo и useCallback используются для простых вычислений.

          Спасибо за вашу внимательность)


          1. BanderasPRO
            03.06.2025 15:00

            Пример вырожденный и не о "смысле", это лишь для демонстрации, где ошибка в вашей статье. Но если честно, не могу понять, зачем вы пытаетесь как-то сместить фокус вашей явной ошибки в статье на вторичные детали в комментариях.

            Вы ведёте себя, как политик... в нехорошем смысле. Это огорчает.


  1. ViktorDark
    03.06.2025 15:00

    Статья хорошая и полезная. Но есть маленький вопрос, а разве мост bridge не заменили на JSI в 0.72 по умолчанию?


    1. SalaarFiend Автор
      03.06.2025 15:00

      Да, всё так, но к сожалению большие проекты не имеют возможности перейти на новую архитектуру. Из-за отключают JSI при переходе на 0.73 и выше.

      Поэтому решил оставить пункт про мост


  1. MainEditor0
    03.06.2025 15:00

    React Native тормозит?

    Отличный, кстати говоря, выбор для главного меню пуск в ОС...


    1. SalaarFiend Автор
      03.06.2025 15:00

      Уже отправил статью МикроМягким :)


      1. MainEditor0
        03.06.2025 15:00

        Хороший бенчмарк-стресстест. Просто открываешь диспетчер задач и тыкаешь кнопку win...


    1. ViktorDark
      03.06.2025 15:00

      Ну кст, после того, как RN переехал на jsi он намного больше может, но конечно все сильно зависит от рук и качества зананий))))
      Хотя чисто по производительности, для меня flutter все еще в лидерах. (kotlin multiplatform не учитываем)


      1. gen1lee
        03.06.2025 15:00

        Осталось параллельность JS завезти в RN эффективную, и была бы вообще сказка.


  1. gen1lee
    03.06.2025 15:00

    Вот хороший пример того, как стоит использовать StyleSheet.create

    Это как раз плохой пример использования StyleSheet.create, так как стили будут создаваться либо на каждый рендер, либо с useMemo на каждое монтирование каждого элемента. То есть если в списке 500 ячеек, то 500 раз.

    В своей статье я писал, как правильно работать со стилями для лучшей производительности.


    1. SalaarFiend Автор
      03.06.2025 15:00

      Отличная статья)
      Спасибо что рассказали, записал себе этот пункт в список оптимизации