mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-12 17:31:06 +08:00
Add back support for lazy tabs and use removeClippedSubviews (#3538)
* Lazy initialization of tabs and move contents off-screen when not active * Make subview clipping and lazy both configurable * Record snapshots again * Update type definition * Remove unused log
This commit is contained in:
@@ -163,6 +163,8 @@ const SimpleTabs = TabNavigator(
|
||||
},
|
||||
},
|
||||
{
|
||||
lazy: true,
|
||||
removeClippedSubviews: true,
|
||||
tabBarOptions: {
|
||||
activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff',
|
||||
},
|
||||
|
||||
@@ -833,6 +833,8 @@ declare module 'react-navigation' {
|
||||
declare type _TabNavigatorConfig = {|
|
||||
...NavigationTabRouterConfig,
|
||||
..._TabViewConfig,
|
||||
lazy?: boolean,
|
||||
removeClippedSubviews?: boolean,
|
||||
containerOptions?: void,
|
||||
|};
|
||||
declare export function TabNavigator(
|
||||
|
||||
@@ -19,6 +19,8 @@ const TabNavigator = (routeConfigs, config = {}) => {
|
||||
tabBarComponent,
|
||||
tabBarPosition,
|
||||
tabBarOptions,
|
||||
lazy,
|
||||
removeClippedSubviews,
|
||||
swipeEnabled,
|
||||
animationEnabled,
|
||||
configureTransition,
|
||||
@@ -31,6 +33,8 @@ const TabNavigator = (routeConfigs, config = {}) => {
|
||||
const navigator = createNavigator(router, routeConfigs, config)(props => (
|
||||
<TabView
|
||||
{...props}
|
||||
lazy={lazy}
|
||||
removeClippedSubviews={removeClippedSubviews}
|
||||
tabBarComponent={tabBarComponent}
|
||||
tabBarPosition={tabBarPosition}
|
||||
tabBarOptions={tabBarOptions}
|
||||
|
||||
@@ -55,13 +55,23 @@ exports[`TabNavigator renders successfully 1`] = `
|
||||
testID={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
"overflow": "hidden",
|
||||
}
|
||||
}
|
||||
/>
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View
|
||||
|
||||
102
packages/react-navigation/src/views/ResourceSavingSceneView.js
vendored
Normal file
102
packages/react-navigation/src/views/ResourceSavingSceneView.js
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import SceneView from './SceneView';
|
||||
|
||||
const FAR_FAR_AWAY = 3000; // this should be big enough to move the whole view out of its container
|
||||
|
||||
export default class ResourceSavingSceneView extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super();
|
||||
|
||||
const key = props.childNavigation.state.key;
|
||||
const focusedIndex = props.navigation.state.index;
|
||||
const focusedKey = props.navigation.state.routes[focusedIndex].key;
|
||||
const isFocused = key === focusedKey;
|
||||
|
||||
this.state = {
|
||||
awake: props.lazy ? isFocused : true,
|
||||
visible: isFocused,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this._actionListener = this.props.navigation.addListener(
|
||||
'action',
|
||||
this._onAction
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._actionListener.remove();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { visible, awake } = this.state;
|
||||
const {
|
||||
childNavigation,
|
||||
navigation,
|
||||
removeClippedSubviews,
|
||||
lazy,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={styles.container}
|
||||
collapsable={false}
|
||||
removeClippedSubviews={
|
||||
Platform.OS === 'android'
|
||||
? removeClippedSubviews
|
||||
: !visible && removeClippedSubviews
|
||||
}
|
||||
>
|
||||
<View style={visible ? styles.innerAttached : styles.innerDetached}>
|
||||
{awake ? <SceneView {...rest} navigation={navigation} /> : null}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_onAction = payload => {
|
||||
// We do not care about transition complete events, they won't actually change the state
|
||||
if (
|
||||
payload.action.type == 'Navigation/COMPLETE_TRANSITION' ||
|
||||
!payload.state
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { routes, index } = payload.state;
|
||||
const key = this.props.childNavigation.state.key;
|
||||
|
||||
if (routes[index].key === key) {
|
||||
if (!this.state.visible) {
|
||||
let nextState = { visible: true };
|
||||
if (!this.state.awake) {
|
||||
nextState.awake = true;
|
||||
}
|
||||
this.setState(nextState);
|
||||
}
|
||||
} else {
|
||||
if (this.state.visible) {
|
||||
this.setState({ visible: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
innerAttached: {
|
||||
flex: 1,
|
||||
},
|
||||
innerDetached: {
|
||||
flex: 1,
|
||||
top: FAR_FAR_AWAY,
|
||||
},
|
||||
});
|
||||
@@ -3,11 +3,13 @@ import { View, StyleSheet, Platform } from 'react-native';
|
||||
import { TabViewAnimated, TabViewPagerPan } from 'react-native-tab-view';
|
||||
import SafeAreaView from 'react-native-safe-area-view';
|
||||
|
||||
import SceneView from '../SceneView';
|
||||
import ResourceSavingSceneView from '../ResourceSavingSceneView';
|
||||
import withCachedChildNavigation from '../../withCachedChildNavigation';
|
||||
|
||||
class TabView extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
lazy: true,
|
||||
removedClippedSubviews: true,
|
||||
// fix for https://github.com/react-native-community/react-native-tab-view/issues/312
|
||||
initialLayout: Platform.select({
|
||||
android: { width: 1, height: 0 },
|
||||
@@ -26,13 +28,14 @@ class TabView extends React.PureComponent {
|
||||
route.routeName
|
||||
);
|
||||
return (
|
||||
<View style={styles.page}>
|
||||
<SceneView
|
||||
screenProps={screenProps}
|
||||
component={TabComponent}
|
||||
navigation={childNavigation}
|
||||
/>
|
||||
</View>
|
||||
<ResourceSavingSceneView
|
||||
lazy={this.props.lazy}
|
||||
removeClippedSubViews={this.props.removeClippedSubviews}
|
||||
screenProps={screenProps}
|
||||
component={TabComponent}
|
||||
navigation={this.props.navigation}
|
||||
childNavigation={childNavigation}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -189,9 +192,4 @@ const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
page: {
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -225,6 +225,8 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
testID={undefined}
|
||||
>
|
||||
<View
|
||||
collapsable={false}
|
||||
removeClippedSubviews={false}
|
||||
style={
|
||||
Object {
|
||||
"flex": 1,
|
||||
@@ -233,25 +235,30 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
}
|
||||
>
|
||||
<View
|
||||
navigation={
|
||||
style={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": undefined,
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"pop": [Function],
|
||||
"popToTop": [Function],
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"key": "s1",
|
||||
"routeName": "s1",
|
||||
},
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
screenProps={undefined}
|
||||
/>
|
||||
>
|
||||
<View
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "s1",
|
||||
"routeName": "s1",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
screenProps={undefined}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</RCTScrollContentView>
|
||||
|
||||
Reference in New Issue
Block a user