我正在开发一个React Native应用程序,使用复合实验导航(CardStack + Tabs)和Redux进行状态管理。我已经能够创建基于选项卡的导航,但现在面临的问题是当我在选项卡之间切换时,组件会被卸载并重新渲染。
问题
假设我向下滚动了若干帖子,当我更改选项卡时,它将从顶部开始。(解决方法可以是将滚动位置存储在Redux状态中)。
这是我用于导航的示例代码 Tabbed Experimental Navigation
我正在开发一个React Native应用程序,使用复合实验导航(CardStack + Tabs)和Redux进行状态管理。我已经能够创建基于选项卡的导航,但现在面临的问题是当我在选项卡之间切换时,组件会被卸载并重新渲染。
问题
假设我向下滚动了若干帖子,当我更改选项卡时,它将从顶部开始。(解决方法可以是将滚动位置存储在Redux状态中)。
这是我用于导航的示例代码 Tabbed Experimental Navigation
您需要改变TabBar的方法。
基本上,您希望每个Tab都有一个Navigator,这样您就可以为每个Tab设置路由堆栈,并且一个Navigator可以包含所有Tabs(TabBar)。您还希望为TabBar设置初始路由堆栈,并在这些路由之间跳转。
了解Navigator方法的区别非常重要。
当您pop
路由时,它将被卸载,并将活动索引移动到最后一个。由于最后一个保留在状态中,因此它将恢复为以前呈现的状态(很可能会发生道具更改)。再次进入popped
路由将完全重新呈现场景(这是您遇到的问题)。
当您push
路由时,没有任何内容被卸载,新路由被安装。
当您jumpToIndex
路由时,同样没有任何内容被卸载,因此在路由之间跳转会将场景恢复为它们以前的状态(如果道具更改,则场景将重新呈现)。
所以,我认为这不正确:
我能够创建基于选项卡的导航,但我现在面临的问题是当我在选项卡之间切换时,组件会被卸载并重新渲染。...你使用了错误的导航操作来卸载路由。NavigationCardStack
不会实际创建它自己的状态,而是从外部传递,这给了你很大的灵活性。而且好消息是,你可以使用 Facebook 提供的 reducers 来执行常见操作(例如 push、pop、jumpToIndex;它们是 Navigation Utils 的一部分)。navigationState
及其 reducers 的完整示例here,因此我不打算解释那个,只是提供解决问题的思路。
import React from 'react';
import { NavigationExperimental, View, Text, StyleSheet } from 'react-native';
const {
CardStack: NavigationCardStack,
StateUtils: NavigationStateUtils,
} = NavigationExperimental;
const style = StyleSheet.create({
screen: {
flex: 1,
},
screenTitle: {
marginTop: 50,
fontSize: 18,
},
pushNewScreenLabel: {
marginVertical: 10,
fontSize: 15,
fontWeight: "bold",
},
goBackLabel: {
fontSize: 15,
},
tabBarWrapper: {
position: 'absolute',
height: 50,
bottom: 0,
left: 0,
right: 0,
top: null,
backgroundColor: 'grey',
flexDirection: 'row',
flex: 0,
alignItems: 'stretch',
},
tabBarItem: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'red',
},
});
export class TabBar extends React.Component {
constructor(props, context) {
super(props, context);
this.jumpToTab = this.jumpToTab.bind(this);
// Create state
this.state = {
navigationState: {
// Active route, will be rendered as default
index: 0,
// "tab-s" represents route objects
routes: [
{ name: 'Tab1', key: '1' },
{ name: 'Tab2', key: '2' },
{ name: 'Tab3', key: '3' },
{ name: 'Tab4', key: '4' }],
},
};
}
jumpToTab(tabIndex) {
const navigationState = NavigationStateUtils.jumpToIndex(this.state.navigationState, tabIndex);
this.setState({ navigationState });
}
renderScene({ scene }) {
return <Tab tab={scene.route} />;
}
render() {
const { navigationState } = this.state;
return (
<View style={style.screen}>
<NavigationCardStack
onNavigate={() => {}}
navigationState={navigationState}
renderScene={this.renderScene}
/>
<View style={style.tabBarWrapper}>
{navigationState.routes.map((route, index) => (
<TabBarItem
key={index}
onPress={this.jumpToTab}
title={route.name}
index={index}
/>
))}
</View>
</View>
);
}
}
class TabBarItem extends React.Component {
static propTypes = {
title: React.PropTypes.string,
onPress: React.PropTypes.func,
index: React.PropTypes.number,
}
constructor(props, context) {
super(props, context);
this.onPress = this.onPress.bind(this);
}
onPress() {
this.props.onPress(this.props.index);
}
render() {
return (
<Text style={style.tabBarItem} onPress={this.onPress}>
{this.props.title}
</Text>);
}
}
class Tab extends React.Component {
static propTypes = {
tab: React.PropTypes.object,
}
constructor(props, context) {
super(props, context);
this.goBack = this.goBack.bind(this);
this.pushRoute = this.pushRoute.bind(this);
this.renderScene = this.renderScene.bind(this);
this.state = {
navigationState: {
index: 0,
routes: [{ key: '0' }],
},
};
}
// As in TabBar use NavigationUtils for this 2 methods
goBack() {
const navigationState = NavigationStateUtils.pop(this.state.navigationState);
this.setState({ navigationState });
}
pushRoute(route) {
const navigationState = NavigationStateUtils.push(this.state.navigationState, route);
this.setState({ navigationState });
}
renderScene({ scene }) {
return (
<Screen
goBack={this.goBack}
goTo={this.pushRoute}
tab={this.props.tab}
screenKey={scene.route.key}
/>
);
}
render() {
return (
<NavigationCardStack
onNavigate={() => {}}
navigationState={this.state.navigationState}
renderScene={this.renderScene}
/>
);
}
}
class Screen extends React.Component {
static propTypes = {
goTo: React.PropTypes.func,
goBack: React.PropTypes.func,
screenKey: React.PropTypes.string,
tab: React.PropTypes.object,
}
constructor(props, context) {
super(props, context);
this.nextScreen = this.nextScreen.bind(this);
}
nextScreen() {
const { goTo, screenKey } = this.props;
goTo({ key: `${parseInt(screenKey) + 1}` });
}
render() {
const { tab, goBack, screenKey } = this.props;
return (
<View style={style.screen}>
<Text style={style.screenTitle}>
{`Tab ${tab.key} - Screen ${screenKey}`}
</Text>
<Text style={style.pushNewScreenLabel} onPress={this.nextScreen}>
Push Screen into this Tab
</Text>
<Text style={style.goBackLabel} onPress={goBack}>
Go back
</Text>
</View>
);
}
}