Use react-native-screens and keep inactive screens in memory, like with tabs

This commit is contained in:
Brent Vatne
2018-10-28 17:29:28 -07:00
committed by satyajit.happy
parent 36893fd572
commit f2eda6ce48
2 changed files with 118 additions and 11 deletions

View File

@@ -1,16 +1,36 @@
import React from 'react';
import { Dimensions } from 'react-native';
import { Dimensions, StyleSheet } from 'react-native';
import { SceneView } from '@react-navigation/core';
// eslint-disable-next-line import/no-unresolved
import { ScreenContainer } from 'react-native-screens';
import DrawerActions from '../routers/DrawerActions';
import DrawerLayout from './DrawerLayout';
import DrawerSidebar from './DrawerSidebar';
import DrawerGestureContext from '../utils/DrawerGestureContext';
import ResourceSavingScene from '../views/ResourceSavingScene';
/**
* Component that renders the drawer.
*/
export default class DrawerView extends React.PureComponent {
static defaultProps = {
lazy: true,
};
static getDerivedStateFromProps(nextProps, prevState) {
const { index } = nextProps.navigation.state;
return {
// Set the current tab to be loaded if it was not loaded before
loaded: prevState.loaded.includes(index)
? prevState.loaded
: [...prevState.loaded, index],
};
}
state = {
loaded: [this.props.navigation.state.index],
drawerWidth:
typeof this.props.navigationConfig.drawerWidth === 'function'
? this.props.navigationConfig.drawerWidth()
@@ -121,11 +141,12 @@ export default class DrawerView extends React.PureComponent {
};
render() {
const { state } = this.props.navigation;
const activeKey = state.routes[state.index].key;
const descriptor = this.props.descriptors[activeKey];
const { drawerLockMode } = descriptor.options;
const { lazy, navigation } = this.props;
const { loaded } = this.state;
const { state } = navigation;
const { routes } = state;
const activeKey = routes[state.index].key;
const { drawerLockMode } = this.props.descriptors[activeKey].options;
return (
<DrawerLayout
@@ -162,13 +183,42 @@ export default class DrawerView extends React.PureComponent {
overlayColor={this.props.navigationConfig.overlayColor}
>
<DrawerGestureContext.Provider value={this.drawerGestureRef}>
<SceneView
navigation={descriptor.navigation}
screenProps={this.props.screenProps}
component={descriptor.getComponent()}
/>
<ScreenContainer style={styles.pages}>
{routes.map((route, index) => {
if (lazy && !loaded.includes(index)) {
// Don't render a screen if we've never navigated to it
return null;
}
const isFocused = navigation.state.index === index;
const descriptor = this.props.descriptors[route.key];
return (
<ResourceSavingScene
key={route.key}
style={[
StyleSheet.absoluteFill,
{ opacity: isFocused ? 1 : 0 },
]}
isVisible={isFocused}
>
<SceneView
navigation={descriptor.navigation}
screenProps={this.props.screenProps}
component={descriptor.getComponent()}
/>
</ResourceSavingScene>
);
})}
</ScreenContainer>
</DrawerGestureContext.Provider>
</DrawerLayout>
);
}
}
const styles = StyleSheet.create({
pages: {
flex: 1,
},
});

View File

@@ -0,0 +1,57 @@
/* @flow */
import * as React from 'react';
import { Platform, StyleSheet, View } from 'react-native';
// eslint-disable-next-line import/no-unresolved
import { Screen, screensEnabled } from 'react-native-screens';
type Props = {
isVisible: boolean,
children: React.Node,
style?: any,
};
const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view out of its container
export default class ResourceSavingScene extends React.Component<Props> {
render() {
if (screensEnabled && screensEnabled()) {
const { isVisible, ...rest } = this.props;
return <Screen active={isVisible ? 1 : 0} {...rest} />;
}
const { isVisible, children, style, ...rest } = this.props;
return (
<View
style={[styles.container, style]}
collapsable={false}
removeClippedSubviews={
// On iOS, set removeClippedSubviews to true only when not focused
// This is an workaround for a bug where the clipped view never re-appears
Platform.OS === 'ios' ? !isVisible : true
}
pointerEvents={isVisible ? 'auto' : 'none'}
{...rest}
>
<View style={isVisible ? styles.attached : styles.detached}>
{children}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
overflow: 'hidden',
},
attached: {
flex: 1,
},
detached: {
flex: 1,
top: FAR_FAR_AWAY,
},
});