Оптимизация компонентов
Мобильные устройства слабее, чем компьютеры. Оптимизация важна.
memo
Мемоизирует компонент. Перерисовывается только при изменении пропсов:
import { memo } from 'react';
export const ItemCard = memo(function ItemCard({ item }: { item: Item }) {
return (
<View>
<Text>{item.title}</Text>
</View>
);
});
Работает только если пропсы не меняются. Объекты и функции нужно мемоизировать отдельно.
useCallback
Мемоизация функций:
const handlePress = useCallback((id: number) => {
navigate('Details', { id });
}, [navigate]);
Без useCallback функция создаётся заново при каждом рендере, и memo не работает.
useMemo
Мемоизация значений:
const sorted = useMemo(
() => [...items].sort((a, b) => a.title.localeCompare(b.title)),
[items]
);
FlatList оптимизация
<FlatList
data={items}
keyExtractor={keyExtractor}
renderItem={renderItem}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
removeClippedSubviews
getItemLayout={(_, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
/>
getItemLayout — если элементы одинаковой высоты.
Мемоизация renderItem
const renderItem = useCallback(
({ item }: { item: Item }) => <ItemCard item={item} />,
[]
);
const keyExtractor = useCallback((item: Item) => item.id.toString(), []);
React.memo с сравнением
const ItemCard = memo(
function ItemCard({ item }: { item: Item }) {
return <View><Text>{item.title}</Text></View>;
},
(prev, next) => prev.item.id === next.item.id && prev.item.title === next.item.title
);
Кастомное сравнение. Используй осторожно — легко ошибиться.
Не создавай объекты в пропсах
// Плохо — новый объект каждый рендер
<ItemCard config={{ showTitle: true }} />
// Хорошо — константа
const config = { showTitle: true };
<ItemCard config={config} />
Избегай inline-функций в FlatList
// Плохо
<FlatList renderItem={({ item }) => <Card item={item} />} />
// Хорошо
const renderItem = useCallback(({ item }) => <Card item={item} />, []);
<FlatList renderItem={renderItem} />
Разделение компонентов
Если компонент большой — раздели на части. Меньше перерисовок:
function Screen() {
const [tab, setTab] = useState('home');
return (
<>
<Header /> {/* не зависит от tab */}
<TabBar current={tab} onChange={setTab} />
{tab === 'home' ? <HomeTab /> : <ProfileTab />}
</>
);
}
Header не перерисовывается при смене вкладки.
Избегай лишних состояний
// Плохо — вычисляемое в state
const [fullName, setFullName] = useState('');
useEffect(() => setFullName(`${first} ${last}`), [first, last]);
// Хорошо — вычисляемое на месте
const fullName = `${first} ${last}`;
Reanimated на UI-потоке
Анимации через Reanimated работают в UI-потоке. Не блокируют JS.
const offset = useSharedValue(0);
const style = useAnimatedStyle(() => ({
transform: [{ translateX: withSpring(offset.value) }],
}));
Избегай тяжёлых вычислений в рендере
// Плохо
function Component({ items }) {
const sorted = items.sort((a, b) => a.price - b.price); // мутация + каждый рендер
return <List items={sorted} />;
}
// Хорошо
function Component({ items }) {
const sorted = useMemo(() => [...items].sort((a, b) => a.price - b.price), [items]);
return <List items={sorted} />;
}
React DevTools Profiler
Показывает, какие компоненты рендерятся и сколько времени это занимает. Используй для поиска проблем.
Flashlight
Инструмент для измерения FPS и производительности на реальном устройстве. Показывает, где проседает.
Итоги
memo,useCallback,useMemo- Оптимизация
FlatList - Мемоизация
renderItem - Не создавай объекты в пропсах
- Разделяй большие компоненты
- Reanimated для плавных анимаций