Fix bug where transitioner freezes when nested state change occurs in the middle of transition

This commit is contained in:
Brent Vatne 2018-10-17 15:26:13 -07:00
parent 5ef3bd4392
commit ee6c6aa08e
3 changed files with 142 additions and 0 deletions

View File

@ -14,6 +14,7 @@ import TransparentStack from './src/TransparentStack';
import ModalStack from './src/ModalStack'; import ModalStack from './src/ModalStack';
import LifecycleInteraction from './src/LifecycleInteraction'; import LifecycleInteraction from './src/LifecycleInteraction';
import GestureInteraction from './src/GestureInteraction'; import GestureInteraction from './src/GestureInteraction';
import SwitchWithStacks from './src/SwitchWithStacks';
// Comment the following two lines to stop using react-native-screens // Comment the following two lines to stop using react-native-screens
import { useScreens } from 'react-native-screens'; import { useScreens } from 'react-native-screens';
@ -42,6 +43,11 @@ const data = [
title: 'Gesture Interaction', title: 'Gesture Interaction',
routeName: 'GestureInteraction', routeName: 'GestureInteraction',
}, },
{
component: SwitchWithStacks,
title: 'Switch with Stacks',
routeName: 'SwitchWithStacks',
},
]; ];
// Cache images // Cache images

View File

@ -0,0 +1,118 @@
import React from 'react';
import {
ActivityIndicator,
Button,
AsyncStorage,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import { createSwitchNavigator } from '@react-navigation/core';
import { createStackNavigator } from 'react-navigation-stack';
class SignInScreen extends React.Component<any, any> {
static navigationOptions = {
title: 'Please sign in',
};
render() {
return (
<View style={styles.container}>
<Button title="Sign in!" onPress={this._signInAsync} />
<Button
title="Go back to other examples"
onPress={() => this.props.navigation.goBack(null)}
/>
<StatusBar barStyle="default" />
</View>
);
}
_signInAsync = async () => {
await AsyncStorage.setItem('userToken', 'abc');
this.props.navigation.navigate('Home');
};
}
class HomeScreen extends React.Component<any, any> {
static navigationOptions = {
title: 'Welcome to the app!',
};
render() {
return (
<View style={styles.container}>
<Button title="Show me more of the app" onPress={this._showMoreApp} />
<Button title="Actually, sign me out :)" onPress={this._signOutAsync} />
<StatusBar barStyle="default" />
</View>
);
}
_showMoreApp = () => {
this.props.navigation.navigate('Other');
};
_signOutAsync = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Auth');
};
}
class OtherScreen extends React.Component<any, any> {
static navigationOptions = {
title: 'Lots of features here',
};
render() {
return (
<View style={styles.container}>
<Button title="I'm done, sign me out" onPress={this._signOutAsync} />
<StatusBar barStyle="default" />
</View>
);
}
_signOutAsync = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Auth');
};
}
class LoadingScreen extends React.Component<any, any> {
componentDidMount() {
this._bootstrapAsync();
}
_bootstrapAsync = async () => {
const userToken = await AsyncStorage.getItem('userToken');
let initialRouteName = userToken ? 'App' : 'Auth';
this.props.navigation.navigate(initialRouteName);
};
render() {
return (
<View style={styles.container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
export default createSwitchNavigator({
Loading: LoadingScreen,
App: AppStack,
Auth: AuthStack,
});

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { Animated, Easing, StyleSheet, View } from 'react-native'; import { Animated, Easing, StyleSheet, View } from 'react-native';
import invariant from '../utils/invariant'; import invariant from '../utils/invariant';
import shallowEqual from '../utils/shallowEqual';
import NavigationScenesReducer from './ScenesReducer'; import NavigationScenesReducer from './ScenesReducer';
@ -86,6 +87,15 @@ class Transitioner extends React.Component {
return; return;
} }
if (
isRouteShallowEqual(
this.props.navigation.state,
nextProps.navigation.state
)
) {
return;
}
const indexHasChanged = const indexHasChanged =
nextProps.navigation.state.index !== this.props.navigation.state.index; nextProps.navigation.state.index !== this.props.navigation.state.index;
if (this._isTransitionRunning) { if (this._isTransitionRunning) {
@ -279,6 +289,14 @@ function isSceneActive(scene) {
return scene.isActive; return scene.isActive;
} }
function isRouteShallowEqual(a, b) {
let routeA = { ...a };
delete routeA.routes;
let routeB = { ...b };
delete routeB.routes;
return shallowEqual(routeA, routeB);
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
main: { main: {
flex: 1, flex: 1,