fix: dispatch pop early when screen is closed with gesture (#336)

fixes #267
This commit is contained in:
Satyajit Sahoo
2020-02-02 23:56:05 +01:00
committed by GitHub
parent 343320783f
commit 3d937d1e65
7 changed files with 44 additions and 19 deletions

View File

@@ -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

View File

@@ -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)}
/> />
) )
)} )}

View File

@@ -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}>

View File

@@ -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;
} }

View File

@@ -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,

View File

@@ -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

View File

@@ -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 = (