Урок 06

Pressable и кнопки

В React Native нет <button>. Вместо него — Pressable, TouchableOpacity, TouchableHighlight.

Pressable

Современный способ. Даёт полный контроль над состоянием нажатия.

import { Pressable, Text, StyleSheet } from 'react-native';

export default function Button() {
    return (
        <Pressable
            onPress={() => console.log('нажато')}
            onLongPress={() => console.log('долгое нажатие')}
            style={({ pressed }) => [
                styles.button,
                pressed && styles.pressed,
            ]}
        >
            <Text style={styles.label}>Нажми</Text>
        </Pressable>
    );
}

style может быть функцией, получающей { pressed }.

Состояния

<Pressable
    style={({ pressed, hovered, focused }) => [
        styles.base,
        pressed && styles.pressed,
        hovered && styles.hovered,
        focused && styles.focused,
    ]}
>
    {({ pressed }) => (
        <Text>{pressed ? 'Нажато' : 'Нажми'}</Text>
    )}
</Pressable>

Или children — функция с состоянием.

TouchableOpacity

Классический вариант с эффектом прозрачности:

import { TouchableOpacity, Text } from 'react-native';

<TouchableOpacity
    onPress={handlePress}
    activeOpacity={0.7}
    disabled={isDisabled}
>
    <Text>Нажми</Text>
</TouchableOpacity>

При нажатии opacity меняется. Просто, но меньше контроля.

TouchableHighlight

Подсветка фона при нажатии:

<TouchableHighlight
    onPress={handlePress}
    underlayColor="#ddd"
>
    <Text>Нажми</Text>
</TouchableHighlight>

Компонент кнопки

import { Pressable, Text, StyleSheet, ActivityIndicator } from 'react-native';

type ButtonProps = {
    label: string;
    onPress: () => void;
    variant?: 'primary' | 'secondary';
    disabled?: boolean;
    loading?: boolean;
};

export function Button({
    label,
    onPress,
    variant = 'primary',
    disabled = false,
    loading = false,
}: ButtonProps) {
    const isDisabled = disabled || loading;

    return (
        <Pressable
            onPress={onPress}
            disabled={isDisabled}
            style={({ pressed }) => [
                styles.base,
                styles[variant],
                pressed && styles.pressed,
                isDisabled && styles.disabled,
            ]}
        >
            {loading ? (
                <ActivityIndicator color="#fff" />
            ) : (
                <Text style={styles.label}>{label}</Text>
            )}
        </Pressable>
    );
}

const styles = StyleSheet.create({
    base: {
        paddingVertical: 12,
        paddingHorizontal: 24,
        borderRadius: 8,
        alignItems: 'center',
        justifyContent: 'center',
        minHeight: 48,
    },
    primary: { backgroundColor: '#c1442e' },
    secondary: { backgroundColor: '#fff', borderWidth: 1, borderColor: '#c1442e' },
    pressed: { opacity: 0.7 },
    disabled: { opacity: 0.5 },
    label: {
        color: '#fff',
        fontSize: 16,
        fontWeight: '600',
    },
});

Если ты уже знаешь React

В вебе — <button onClick={...}>. В React Native — <Pressable onPress={...}>. Название события другое: onPress вместо onClick.

Другие события

  • onPress — клик
  • onLongPress — долгое нажатие
  • onPressIn — начало нажатия
  • onPressOut — конец нажатия
  • onHoverIn / onHoverOut — только с мышью (web, iPad)

Доступность

<Pressable
    onPress={onPress}
    accessibilityRole="button"
    accessibilityLabel="Отправить форму"
    accessibilityHint="Отправляет данные на сервер"
    accessibilityState={{ disabled: isDisabled }}
>

Итоги

  • Нет <button> — используй Pressable
  • onPress вместо onClick
  • style может быть функцией
  • TouchableOpacity — простой вариант
  • ActivityIndicator для загрузки