Урок 08

Навигация в компонентах

Компоненты часто нужны навигацию для переходов. Разберём базовые приёмы.

useNavigation

Хук для доступа к навигации в любом компоненте внутри навигатора:

import { useNavigation } from '@react-navigation/native';

function BackButton() {
    const navigation = useNavigation();

    return (
        <Pressable onPress={() => navigation.goBack()}>
            <Text>Назад</Text>
        </Pressable>
    );
}

Не требует передачи navigation через пропсы.

useRoute

Параметры текущего экрана:

import { useRoute } from '@react-navigation/native';

function Details() {
    const route = useRoute();
    const { id } = route.params as { id: number };

    return <Text>ID: {id}</Text>;
}

Navigate между экранами

navigation.navigate('Details', { id: 5 });

Если экран уже в стеке — вернётся на него. Если нет — добавит.

Push

navigation.push('Details', { id: 5 });

Всегда добавляет новый экран, даже если такой же уже есть.

Replace

navigation.replace('Login');

Заменяет текущий экран. Назад вернуться нельзя.

Go back

navigation.goBack();
navigation.pop();        // то же
navigation.popToTop();   // вернуться к первому

Параметры с типами

type RootStackParamList = {
    Home: undefined;
    Details: { id: number; title?: string };
    Profile: { userId: string };
};

type NavigationProp = NativeStackNavigationProp<RootStackParamList>;

function DetailsScreen() {
    const route = useRoute<RouteProp<RootStackParamList, 'Details'>>();
    const navigation = useNavigation<NavigationProp>();

    const { id, title } = route.params;

    return (
        <Pressable onPress={() => navigation.navigate('Profile', { userId: 'abc' })}>
            <Text>{title ?? `Детали ${id}`}</Text>
        </Pressable>
    );
}

Хедер с кнопкой

function Screen() {
    const navigation = useNavigation();

    useLayoutEffect(() => {
        navigation.setOptions({
            headerRight: () => (
                <Pressable onPress={() => navigation.navigate('Settings')}>
                    <Ionicons name="settings" size={24} />
                </Pressable>
            ),
        });
    }, [navigation]);

    return <View />;
}

useFocusEffect

Эффект при фокусе экрана:

import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';

function Screen() {
    useFocusEffect(
        useCallback(() => {
            console.log('экран в фокусе');
            return () => console.log('экран потерял фокус');
        }, [])
    );

    return <View />;
}

Используется для обновления данных при возврате на экран.

If focused

import { useIsFocused } from '@react-navigation/native';

function Screen() {
    const isFocused = useIsFocused();

    return isFocused ? <VideoPlayer /> : <VideoPaused />;
}

Before remove

Предотвратить уход с экрана:

useEffect(() => {
    const unsubscribe = navigation.addListener('beforeRemove', (e) => {
        if (!hasUnsavedChanges) return;

        e.preventDefault();

        Alert.alert('Уйти?', 'Изменения не сохранятся', [
            { text: 'Остаться', style: 'cancel' },
            { text: 'Уйти', onPress: () => navigation.dispatch(e.data.action) },
        ]);
    });

    return unsubscribe;
}, [navigation, hasUnsavedChanges]);

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

В вебе — useNavigate из React Router. В React Native — useNavigation из React Navigation. Логика похожа, API отличается.

Итоги

  • useNavigation в компонентах
  • useRoute для параметров
  • navigate, push, replace, goBack
  • useLayoutEffect для хедера
  • useFocusEffect для обновления при фокусе
  • beforeRemove для подтверждения выхода