mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-04-28 20:35:19 +08:00
fix: dispatch pop early when screen is closed with gesture (#336)
fixes #267
This commit is contained in:
2
.github/workflows/expo-preview.yml
vendored
2
.github/workflows/expo-preview.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 10.x
|
||||||
|
|
||||||
- name: Setup Expo
|
- name: Setup Expo
|
||||||
uses: expo/expo-github-action@v5
|
uses: expo/expo-github-action@v5
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ export default function App() {
|
|||||||
<List.Item
|
<List.Item
|
||||||
key={name}
|
key={name}
|
||||||
title={SCREENS[name].title}
|
title={SCREENS[name].title}
|
||||||
onPress={() => navigation.push(name)}
|
onPress={() => navigation.navigate(name)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
Route,
|
Route,
|
||||||
ParamListBase,
|
ParamListBase,
|
||||||
} from '@react-navigation/native';
|
} from '@react-navigation/native';
|
||||||
import { StackNavigationState } from '@react-navigation/routers';
|
|
||||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
@@ -28,10 +27,10 @@ export type Props = {
|
|||||||
layout: Layout;
|
layout: Layout;
|
||||||
insets: EdgeInsets;
|
insets: EdgeInsets;
|
||||||
scenes: (Scene<Route<string>> | undefined)[];
|
scenes: (Scene<Route<string>> | undefined)[];
|
||||||
state: StackNavigationState;
|
|
||||||
getPreviousRoute: (props: {
|
getPreviousRoute: (props: {
|
||||||
route: Route<string>;
|
route: Route<string>;
|
||||||
}) => Route<string> | undefined;
|
}) => Route<string> | undefined;
|
||||||
|
getFocusedRoute: () => Route<string>;
|
||||||
onContentHeightChange?: (props: {
|
onContentHeightChange?: (props: {
|
||||||
route: Route<string>;
|
route: Route<string>;
|
||||||
height: number;
|
height: number;
|
||||||
@@ -46,14 +45,14 @@ export default function HeaderContainer({
|
|||||||
scenes,
|
scenes,
|
||||||
layout,
|
layout,
|
||||||
insets,
|
insets,
|
||||||
state,
|
getFocusedRoute,
|
||||||
getPreviousRoute,
|
getPreviousRoute,
|
||||||
onContentHeightChange,
|
onContentHeightChange,
|
||||||
gestureDirection,
|
gestureDirection,
|
||||||
styleInterpolator,
|
styleInterpolator,
|
||||||
style,
|
style,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const focusedRoute = state.routes[state.index];
|
const focusedRoute = getFocusedRoute();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View pointerEvents="box-none" style={style}>
|
<View pointerEvents="box-none" style={style}>
|
||||||
|
|||||||
@@ -133,6 +133,8 @@ export default class Card extends React.Component<Props> {
|
|||||||
|
|
||||||
private interactionHandle: number | undefined;
|
private interactionHandle: number | undefined;
|
||||||
|
|
||||||
|
private pendingGestureCallback: number | undefined;
|
||||||
|
|
||||||
private animate = ({
|
private animate = ({
|
||||||
closing,
|
closing,
|
||||||
velocity,
|
velocity,
|
||||||
@@ -161,6 +163,8 @@ export default class Card extends React.Component<Props> {
|
|||||||
this.setPointerEventsEnabled(!closing);
|
this.setPointerEventsEnabled(!closing);
|
||||||
this.handleStartInteraction();
|
this.handleStartInteraction();
|
||||||
|
|
||||||
|
clearTimeout(this.pendingGestureCallback);
|
||||||
|
|
||||||
onTransitionStart?.({ closing });
|
onTransitionStart?.({ closing });
|
||||||
animation(gesture, {
|
animation(gesture, {
|
||||||
...spec.config,
|
...spec.config,
|
||||||
@@ -171,6 +175,8 @@ export default class Card extends React.Component<Props> {
|
|||||||
}).start(({ finished }) => {
|
}).start(({ finished }) => {
|
||||||
this.handleEndInteraction();
|
this.handleEndInteraction();
|
||||||
|
|
||||||
|
clearTimeout(this.pendingGestureCallback);
|
||||||
|
|
||||||
if (finished) {
|
if (finished) {
|
||||||
if (closing) {
|
if (closing) {
|
||||||
onClose();
|
onClose();
|
||||||
@@ -221,6 +227,7 @@ export default class Card extends React.Component<Props> {
|
|||||||
}: PanGestureHandlerGestureEvent) => {
|
}: PanGestureHandlerGestureEvent) => {
|
||||||
const {
|
const {
|
||||||
layout,
|
layout,
|
||||||
|
onClose,
|
||||||
onGestureBegin,
|
onGestureBegin,
|
||||||
onGestureCanceled,
|
onGestureCanceled,
|
||||||
onGestureEnd,
|
onGestureEnd,
|
||||||
@@ -266,6 +273,16 @@ export default class Card extends React.Component<Props> {
|
|||||||
: false;
|
: false;
|
||||||
|
|
||||||
this.animate({ closing, velocity });
|
this.animate({ closing, velocity });
|
||||||
|
|
||||||
|
if (closing) {
|
||||||
|
// We call onClose with a delay to make sure that the animation has already started
|
||||||
|
// This will make sure that the state update caused by this doesn't affect start of animation
|
||||||
|
this.pendingGestureCallback = (setTimeout(
|
||||||
|
onClose,
|
||||||
|
32
|
||||||
|
) as any) as number;
|
||||||
|
}
|
||||||
|
|
||||||
onGestureEnd?.();
|
onGestureEnd?.();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
ViewStyle,
|
ViewStyle,
|
||||||
Platform,
|
Platform,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { StackNavigationState } from '@react-navigation/routers';
|
|
||||||
import { Route, useTheme } from '@react-navigation/native';
|
import { Route, useTheme } from '@react-navigation/native';
|
||||||
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
import { Props as HeaderContainerProps } from '../Header/HeaderContainer';
|
||||||
import Card from './Card';
|
import Card from './Card';
|
||||||
@@ -23,7 +22,6 @@ type Props = TransitionPreset & {
|
|||||||
gesture: Animated.Value;
|
gesture: Animated.Value;
|
||||||
previousScene?: Scene<Route<string>>;
|
previousScene?: Scene<Route<string>>;
|
||||||
scene: Scene<Route<string>>;
|
scene: Scene<Route<string>>;
|
||||||
state: StackNavigationState;
|
|
||||||
safeAreaInsetTop: number;
|
safeAreaInsetTop: number;
|
||||||
safeAreaInsetRight: number;
|
safeAreaInsetRight: number;
|
||||||
safeAreaInsetBottom: number;
|
safeAreaInsetBottom: number;
|
||||||
@@ -34,6 +32,7 @@ type Props = TransitionPreset & {
|
|||||||
getPreviousRoute: (props: {
|
getPreviousRoute: (props: {
|
||||||
route: Route<string>;
|
route: Route<string>;
|
||||||
}) => Route<string> | undefined;
|
}) => Route<string> | undefined;
|
||||||
|
getFocusedRoute: () => Route<string>;
|
||||||
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
renderHeader: (props: HeaderContainerProps) => React.ReactNode;
|
||||||
renderScene: (props: { route: Route<string> }) => React.ReactNode;
|
renderScene: (props: { route: Route<string> }) => React.ReactNode;
|
||||||
onOpenRoute: (props: { route: Route<string> }) => void;
|
onOpenRoute: (props: { route: Route<string> }) => void;
|
||||||
@@ -78,6 +77,7 @@ function CardContainer({
|
|||||||
gestureResponseDistance,
|
gestureResponseDistance,
|
||||||
gestureVelocityImpact,
|
gestureVelocityImpact,
|
||||||
getPreviousRoute,
|
getPreviousRoute,
|
||||||
|
getFocusedRoute,
|
||||||
headerMode,
|
headerMode,
|
||||||
headerShown,
|
headerShown,
|
||||||
headerStyleInterpolator,
|
headerStyleInterpolator,
|
||||||
@@ -101,7 +101,6 @@ function CardContainer({
|
|||||||
safeAreaInsetRight,
|
safeAreaInsetRight,
|
||||||
safeAreaInsetTop,
|
safeAreaInsetTop,
|
||||||
scene,
|
scene,
|
||||||
state,
|
|
||||||
transitionSpec,
|
transitionSpec,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -203,8 +202,8 @@ function CardContainer({
|
|||||||
layout,
|
layout,
|
||||||
insets,
|
insets,
|
||||||
scenes: [previousScene, scene],
|
scenes: [previousScene, scene],
|
||||||
state,
|
|
||||||
getPreviousRoute,
|
getPreviousRoute,
|
||||||
|
getFocusedRoute,
|
||||||
gestureDirection,
|
gestureDirection,
|
||||||
styleInterpolator: headerStyleInterpolator,
|
styleInterpolator: headerStyleInterpolator,
|
||||||
onContentHeightChange: onHeaderHeightChange,
|
onContentHeightChange: onHeaderHeightChange,
|
||||||
|
|||||||
@@ -398,6 +398,12 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private getFocusedRoute = () => {
|
||||||
|
const { state } = this.props;
|
||||||
|
|
||||||
|
return state.routes[state.index];
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
mode,
|
mode,
|
||||||
@@ -581,7 +587,6 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
gesture={gesture}
|
gesture={gesture}
|
||||||
scene={scene}
|
scene={scene}
|
||||||
previousScene={previousScene}
|
previousScene={previousScene}
|
||||||
state={state}
|
|
||||||
safeAreaInsetTop={safeAreaInsetTop}
|
safeAreaInsetTop={safeAreaInsetTop}
|
||||||
safeAreaInsetRight={safeAreaInsetRight}
|
safeAreaInsetRight={safeAreaInsetRight}
|
||||||
safeAreaInsetBottom={safeAreaInsetBottom}
|
safeAreaInsetBottom={safeAreaInsetBottom}
|
||||||
@@ -596,6 +601,7 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
headerHeight={headerHeights[route.key]}
|
headerHeight={headerHeights[route.key]}
|
||||||
onHeaderHeightChange={this.handleHeaderLayout}
|
onHeaderHeightChange={this.handleHeaderLayout}
|
||||||
getPreviousRoute={getPreviousRoute}
|
getPreviousRoute={getPreviousRoute}
|
||||||
|
getFocusedRoute={this.getFocusedRoute}
|
||||||
headerMode={headerMode}
|
headerMode={headerMode}
|
||||||
headerShown={headerShown}
|
headerShown={headerShown}
|
||||||
headerTransparent={headerTransparent}
|
headerTransparent={headerTransparent}
|
||||||
@@ -619,8 +625,8 @@ export default class CardStack extends React.Component<Props, State> {
|
|||||||
layout,
|
layout,
|
||||||
insets: { top, right, bottom, left },
|
insets: { top, right, bottom, left },
|
||||||
scenes,
|
scenes,
|
||||||
state,
|
|
||||||
getPreviousRoute,
|
getPreviousRoute,
|
||||||
|
getFocusedRoute: this.getFocusedRoute,
|
||||||
onContentHeightChange: this.handleHeaderLayout,
|
onContentHeightChange: this.handleHeaderLayout,
|
||||||
gestureDirection:
|
gestureDirection:
|
||||||
focusedOptions.gestureDirection !== undefined
|
focusedOptions.gestureDirection !== undefined
|
||||||
|
|||||||
@@ -314,14 +314,18 @@ class StackView extends React.Component<Props, State> {
|
|||||||
source: route.key,
|
source: route.key,
|
||||||
target: state.key,
|
target: state.key,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// We need to clean up any state tracking the route and pop it immediately
|
||||||
|
this.setState(state => ({
|
||||||
|
routes: state.routes.filter(r => r.key !== route.key),
|
||||||
|
openingRouteKeys: state.openingRouteKeys.filter(
|
||||||
|
key => key !== route.key
|
||||||
|
),
|
||||||
|
closingRouteKeys: state.closingRouteKeys.filter(
|
||||||
|
key => key !== route.key
|
||||||
|
),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to clean up any state tracking the route and pop it immediately
|
|
||||||
this.setState(state => ({
|
|
||||||
routes: state.routes.filter(r => r.key !== route.key),
|
|
||||||
openingRouteKeys: state.openingRouteKeys.filter(key => key !== route.key),
|
|
||||||
closingRouteKeys: state.closingRouteKeys.filter(key => key !== route.key),
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleTransitionStart = (
|
private handleTransitionStart = (
|
||||||
|
|||||||
Reference in New Issue
Block a user