#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}
/>
);
};