Прослушивание навигации с реакцией на отклонение события

#javascript #reactjs #react-native #react-redux #react-navigation

#javascript #reactjs #react-native #реагирование-сокращение #реагирование-навигация

Вопрос:

У меня есть модальная навигация в react, написанная как функциональный компонент. На IOS его можно отключить, просто проведя пальцем вниз. Итак, у меня нет кнопки «Назад» или чего-то еще.

Я хотел бы иметь возможность прослушивать событие dismiss в модальном, поэтому я могу вызвать функцию, которая очищает (redux) состояние и извлекает новые данные при закрытии модального.

navigation.addListener('willBlur') Событие не работает, потому что оно также запускается, когда я открываю другой модал из этого модала.

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

     const filters = useSelector(taskFilters);
    const dispatch = useDispatch();
    const isFirstRun = useRef(true);
    useEffect(() => {
        // Skip first render
        if (isFirstRun.current) {
            isFirstRun.current = false;
            return;
        }
        // effect cleanup
        return () => {
            dispatch(cleanUpAndFetch(filters));
        };
    }, [filters]);

  

Если мне это нравится, оно вызывается только при отключении компонента. Но теперь у меня старая версия фильтров, потому что они не предоставляются как зависимости. И я хочу вызывать cleanUpAndFetch только в том случае, если данные были изменены, поэтому мне все равно нужно позаботиться и об этом

   useEffect(() => {
        return () => {
            dispatch(cleanUpAndFetch(filters));
        };
    }, []);
  

Подводя итог. Есть ли способ прослушивания навигации с реакцией на отклонение события?

Я использую новейшую версию Expo и React Navigation v.4

Редактировать:

Модальный создается конфигурацией навигации, что-то вроде этого:

 export const RootStack = createStackNavigator(
    {
        Main: {
            screen: RootSwitchStack,
            navigationOptions: {
                headerShown: false,
                headerBackTitleVisible: false
            }
        },
        MyModal,
        ChildModal // Gets opened from MyModal
    },
    {
        initialRouteName: 'Main',
        mode: 'modal',
        defaultNavigationOptions: {
            ...headerSetup,
            ...modalTransitions
        }
    }
);
  

Где MyModal тот, где мне нужно что-то вроде onRequestClose

Ответ №1:

Событие ‘Dismiss’ в iOS может обрабатываться beforeRemove событием навигации / прослушивателем в версии 5.x (не уверен, доступно ли оно в 4.x).

Ознакомьтесь с этими документами:https://reactnavigation.org/docs/navigation-events /

Вы можете прослушать это событие несколькими способами, но вот один пример:

 screenListeners = {
    focus: () => {
        // do something when screen opens amp; is focused
    },
    beforeRemove: () => {
        // do something when scree is closed OR dismissed (in iOS)
    }
};
  

и при рендеринге позже…

 <RootStack.Navigator mode="modal">
    <RootStack.Screen
        name="MyScreen"
        component={MyScreenStackNavigator}
        listeners={screenListeners}
    />
</RootStack.Navigator>
  

Другой пример из документов:https://reactnavigation.org/docs/navigation-events/#listeners-prop-on-screen

Комментарии:

1. Отлично! Попробуем это, когда обновимся до версии 5.0

2. здесь также может быть полезен хук useFocusEffect.

Ответ №2:

Я все еще ищу решение, но я реализовал следующую работу:

Модальный режим IOS будет использовать переход по умолчанию и может быть отклонен только с помощью кнопки возврата заголовка.

 import {
    createStackNavigator,
    TransitionPresets
} from 'react-navigation-stack';
...
export const RootStack = createStackNavigator(
    {
        Main: {
            screen: RootSwitchStack,
            navigationOptions: {
                headerShown: false,
                headerBackTitleVisible: false
            }
        },
        MyModal: { 
            screen: MyModal,
            navigationOptions: ({navigation}) => ({
                // Our previous were ModalPresentationIOS, but we cannot know
                // when the component has been dismissed, so we use a modal with 
                // back button
                ...TransitionPresets.DefaultTransition,
                headerLeft: () => <HeaderBack navigation={navigation} />
            })
        },
        ChildModal // Gets opened from MyModal
    },
    {
        initialRouteName: 'Main',
        mode: 'modal',
        defaultNavigationOptions: {
            ...headerSetup,
            ...modalTransitions
        }
    }
);

import { HeaderBackButton } from 'react-navigation-stack';

export const HeaderBack = ({
    navigation
}: {
    navigation: NavigationStackProp;
}) => {
    const filters = useSelector(taskFilters);
    const [initialFilters] = useState(filters);

    const dispatch = useDispatch();

    // We use a bit more complex comparison, but geist of it is the same
    const isFiltersTheSameAsInitial = initialFilters === filters;

    return (
        <HeaderBackButton
            labelVisible={false}
            onPress={() => {
                if (!isFiltersTheSameAsInitial) {
                    dispatch(cleanUpAndFetch(filters));
                }
                navigation.pop();
            }}
            tintColor={colors.black}
        />
    );
};