Кастомные компоненты
Создание переиспользуемых компонентов с пропсами.
Простой компонент
type CardProps = {
children: React.ReactNode;
};
export function Card({ children }: CardProps) {
return <View style={styles.card}>{children}</View>;
}
const styles = StyleSheet.create({
card: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
},
});
С вариантами
type BadgeProps = {
label: string;
variant?: 'success' | 'error' | 'warning';
};
export function Badge({ label, variant = 'success' }: BadgeProps) {
return (
<View style={[styles.base, styles[variant]]}>
<Text style={styles.label}>{label}</Text>
</View>
);
}
const styles = StyleSheet.create({
base: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 999 },
success: { backgroundColor: '#d1fae5' },
error: { backgroundColor: '#fee2e2' },
warning: { backgroundColor: '#fef3c7' },
label: { fontSize: 12, fontWeight: '600' },
});
С стилями извне
type CardProps = {
children: React.ReactNode;
style?: StyleProp<ViewStyle>;
};
export function Card({ children, style }: CardProps) {
return <View style={[styles.card, style]}>{children}</View>;
}
StyleProp<ViewStyle> — тип для стилей. Позволяет передавать массив, объект, undefined.
С событиями
type ListItemProps = {
title: string;
subtitle?: string;
onPress: () => void;
};
export function ListItem({ title, subtitle, onPress }: ListItemProps) {
return (
<Pressable
onPress={onPress}
style={({ pressed }) => [styles.item, pressed && styles.pressed]}
>
<Text style={styles.title}>{title}</Text>
{subtitle ? <Text style={styles.subtitle}>{subtitle}</Text> : null}
</Pressable>
);
}
С иконкой
type IconButtonProps = {
icon: keyof typeof Ionicons.glyphMap;
onPress: () => void;
size?: number;
color?: string;
};
export function IconButton({ icon, onPress, size = 24, color = '#333' }: IconButtonProps) {
return (
<Pressable onPress={onPress} hitSlop={8}>
<Ionicons name={icon} size={size} color={color} />
</Pressable>
);
}
hitSlop увеличивает область нажатия без изменения размера.
Состояние внутри
export function Collapsible({ title, children }: { title: string; children: React.ReactNode }) {
const [open, setOpen] = useState(false);
return (
<View>
<Pressable onPress={() => setOpen((o) => !o)} style={styles.header}>
<Text style={styles.headerText}>{title}</Text>
<Ionicons name={open ? 'chevron-up' : 'chevron-down'} size={20} />
</Pressable>
{open ? <View style={styles.content}>{children}</View> : null}
</View>
);
}
Compound components
function CardRoot({ children, style }: { children: React.ReactNode; style?: StyleProp<ViewStyle> }) {
return <View style={[styles.card, style]}>{children}</View>;
}
function CardHeader({ children }: { children: React.ReactNode }) {
return <View style={styles.header}>{children}</View>;
}
function CardBody({ children }: { children: React.ReactNode }) {
return <View style={styles.body}>{children}</View>;
}
export const Card = Object.assign(CardRoot, {
Header: CardHeader,
Body: CardBody,
});
// использование
<Card>
<Card.Header><Text>Заголовок</Text></Card.Header>
<Card.Body><Text>Контент</Text></Card.Body>
</Card>
memo
Мемоизация для оптимизации:
export const ItemCard = memo(function ItemCard({ item }: { item: Item }) {
return (
<View>
<Text>{item.title}</Text>
</View>
);
});
Применяй там, где компонент часто перерисовывается с теми же пропсами.
Если ты уже знаешь React
Всё то же, что в вебе: пропсы, children, композиция. Отличия — в стилях (style вместо className) и компонентах (View вместо div).
Итоги
- Пропсы и children как в React
StyleProp<ViewStyle>для стилей извнеhitSlopдля увеличения области нажатия- Compound components для сложных UI
memoдля оптимизации