Resetting the navigation stack when changing tab (StackNavigators nested in TabNavigator) - react-navigation

I have two StackNavigator. Movies:
const MoviesStackNavigator = createStackNavigator({
Movies: Movies,
MovieDetails: MovieDetails,
},{
initialRouteName: 'Movies',
})
and actors:
const ActorsStackNavigator = createStackNavigator({
Actors: Actors,
ActorDetails: ActorDetails,
},{
initialRouteName: 'Actors',
})
I am using them inside a TabNavigator:
const RootTabs = createBottomTabNavigator({
Movies: {
screen: MoviesStackNavigator,
navigationOptions: {
tabBarLabel: 'Movies'
}
},
Actors: {
screen: ActorsStackNavigator,
navigationOptions: {
tabBarLabel: 'Actors'
}
},
})
When the user changes tab, I want him to arrive to the root component of that tab (Movies or Actors), not to a stacked component (MoviesDetails or ActorsDetails). In other word, when a user quits a tab, the StackNavigator in that tab should be resetted to the root component.
My first approach was to use StackActions.reset when initializing the root component in a tab to try to reset the other tab (that the user just quit) but the library prevents interacting between navigators, at least in that way.
So, any ideas?

My solution was to use tabBarOnPress:
const RootTabs = createBottomTabNavigator({
Movies: {
screen: MoviesStackNavigator,
navigationOptions: {
tabBarLabel: 'Movies',
tabBarOnPress: ({navigation, defaultHandler}: any) => {
navigation.navigate('Actors')
defaultHandler()
},
}
},
Actors: {
screen: ActorsStackNavigator,
navigationOptions: {
tabBarLabel: 'Actors',
tabBarOnPress: ({navigation, defaultHandler}: any) => {
navigation.navigate('Movies')
defaultHandler()
},
}
},
})

Related

ReactNavigator -StackNavigator how to use diffrent animation with only specific screen

using
"react-navigation": "^4.0.10",
"react-navigation-stack": "^1.10.3",
import {
createStackNavigator, StackViewTransitionConfigs,
} from 'react-navigation-stack';
const HomeStackNavigator = createStackNavigator({
List:ListContainer,
Detail:DetailContainer,
Modal: {
screen: ModalContainer,
navigationOptions: {
...StackViewTransitionConfigs.ModalSlideFromBottomIOS,
},
},
}, {
mode: 'card',
headerMode: 'none',
defaultNavigationOptions: {
gestureEnabled: true,
...StackViewTransitionConfigs.SlideFromRightIOS,
},
});
All other screens open in the form of a card, and I want to open only one specific screen in modal form.
i tried
const HomeStackNavigator = createStackNavigator({
List:ListContainer,
Detail:DetailContainer,
},
}, {
mode: 'card',
headerMode: 'none',
defaultNavigationOptions: {
gestureEnabled: true,
...StackViewTransitionConfigs.SlideFromRightIOS,
},
});
const HomeStackNavigator2 = createStackNavigator({
Home:HomeStackNavigator,
Modal: {
screen: ModalContainer,
},
}, {
mode: 'modal',
headerMode: 'none',
});
The first code failed because all the arcs were opened in card format.
In the second code I tried, the screen I wanted was opened in modal form, but when I moved from modal to detail, the modal was closed.
In the first code, is it possible to open only the modal screen in modal form?
I solved it this way.
import {
createStackNavigator, StackViewTransitionConfigs, StackViewStyleInterpolator,
} from 'react-navigation-stack';
const transitionConfigs = (props) => {
const { scene } = props;
const { route } = scene;
const params = route.params || {};
const transition = params.transition || 'forHorizontal';
if (transition === 'forVertical') {
return StackViewTransitionConfigs.ModalSlideFromBottomIOS;
}
return StackViewTransitionConfigs.SlideFromRightIOS;
};
const HomeStackNavigator = createStackNavigator({
List:ListContainer,
Detail:DetailContainer,
Modal: {
screen: ModalContainer,
},
}, {
mode: 'card',
headerMode: 'none',
transitionConfig:transitionConfigs,
});
Change animation by sending trasition:'forVertical' to params when navigate.

React Navigation: setParams in Nested Navigator

In my React Native application, I use React Navigation.
It's an app that enables the user to search an underlying database, i.e. for names. The GIF below illustrates the navigation.
From the landing screen, Go to search button is pressed (Main Stack Navigator) --> The Header appears, which is alright.
On the second screen, there is a bottomTabNavigator, where names is chosen (in names, there is a second StackNavigator nested).
This leads to the third screen. Here, three cards are shown. With the help of the second StackNavigator, clicking on Mehr opens a details screen.
What I want to achieve is that the Header of the first StackNavigator (that one at the top) disappears as soon as the user opens the details screen.
You see a button there because in the first step, I wanted to let the Header disappear on button click.
The below code works if it is implemented in a screen that is derived from the first StackNavigator directly. But because I am inside a nested navigator, it does not work anymore.
Here is the code:
App.tsx:
imports ...
class RootComponent extends React.Component {
render() {
const image = require('./assets/images/corrieBackground3.png');
console.log('calling the store', this.props.resultValue); // undefined
return (
<View style={styles.container}>
<LandingPage />
</View>
);
}
}
const RootStack = createStackNavigator(
{
LandingPage: {
screen: RootComponent,
navigationOptions: {
header: null,
},
},
SearchScreen: {
screen: SearchScreen,
navigationOptions: {
title: 'I SHOULD DISAPPEAR',
},
},
},
{
initialRouteName: 'LandingPage',
},
);
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
TwoTabs.tsx (for the 2nd screen):
imports ...
const SearchBarStack = createStackNavigator(
{
SearchBar: {
screen: SearchBar,
navigationOptions: {
header: null,
},
},
Details: {
screen: Details,
navigationOptions: {
title: 'I am here, above header disapear',
},
},
},
{
initialRouteName: 'SearchBar',
},
);
const TabNavigator = createBottomTabNavigator(
{
One: {
screen: SearchCriteria,
navigationOptions: {
tabBarLabel: 'criteria',
},
},
Two: {
screen: SearchBarStack,
navigationOptions: {
tabBarLabel: 'names',
},
},
},
);
const TabLayout = createAppContainer(TabNavigator);
type Props = {};
const TwoTabsHorizontal: React.FC<Props> = ({}) => {
return (
<View>
<TabLayout />
</View>
);
};
export default TwoTabs;
SearchBar.tsx (3rd screens skeleton):
import ...
type Props = {};
const SearchBar: React.FC<Props> = () => {
// logic to perform database query
return (
<View>
<ScrollView>
... logic
<SearchResult></SearchResult> // component that renders 3 cards
</ScrollView>
</View>
);
};
export default SearchBar;
Card.tsx (card rendered by SearchResult):
imports ...
type Props = {
title: string;
navigation: any;
};
const Card: React.FC<Props> = ({title, navigation}) => {
return (
<Content>
<Card>
<CardItem>
<Right>
<Button
transparent
onPress={() => navigation.navigate('Details')}>
<Text>Mehr</Text>
</Button>
</Right>
</CardItem>
</Card>
</Content>
);
};
export default withNavigation(Card);
And finally, the Details screen together with its Content. Here, the Header from the first StackNavigator should be hidden.
imports ...
type Props = {};
const Details: React.FC<Props> = ({}) => {
return (
<View>
<Content></Content>
</View>
);
};
export default Details;
imports ...
type Props = {
navigation: any;
};
class Content extends React.Component {
state = {
showHeader: false,
};
static navigationOptions = ({navigation}) => {
const {params} = navigation.state;
return params;
};
hideHeader = (hide: boolean) => {
this.props.navigation.setParams({
headerShown: !hide,
});
console.log('props ', this.props.navigation);
};
render() {
return (
<View>
<View>
</View>
<Button
title={'Press me and the header will disappear!'}
onPress={() => {
this.setState({showHeader: !this.state.showHeader}, () =>
this.hideHeader(this.state.showHeader),
);
}}
/>
</View>
);
}
}
export default withNavigation(CardExtended);
Maybe someone has an idea?

tabBarOnPress stopped working react-navigation 3.3.2

After upgrading to react-navigation 3.3.2 the tabBarOnPress no longer fires. Code snippet below. Any ideas?
const Tabs = createBottomTabNavigator(
{
HomeStack,
WallStack,
MemeStack,
},
{
navigationOptions: ({navigation}) => ({
tabBarOnPress: () => {
console.log('tab pressed');
}
})
}
);
export default DrawerNav = createDrawerNavigator({
Tabs: Tabs
},{
drawerBackgroundColor: Colors.grayDark,
contentComponent: Sidebar
})
tabBarOnPress is working, but I'm using it like this. You need to pass it as a property of each stack. (don't forget to call the defaultHandler to keep the default behavior:
const DashboardStack = createStackNavigator(
{
Dashboard,
}
);
DashboardStack.navigationOptions = {
tabBarOnPress({ navigation, defaultHandler }) {
// do something
defaultHandler();
},
};
const ProfileStack = createStackNavigator(
{
Profile,
AccountInfo,
Membership,
Browser,
ConnectedAccounts,
}
);
ProfileStack.navigationOptions = {
tabBarOnPress({ navigation, defaultHandler }) {
// do something
defaultHandler();
},
};
const SpendingStack = createStackNavigator(
{
Budget,
Transactions,
}
);
SpendingStack.navigationOptions = {
tabBarOnPress({ navigation, defaultHandler }) {
// do something
defaultHandler();
},
};
export default createBottomTabNavigator(
{
SpendingStack,
DashboardStack,
ProfileStack,
}
);

How to update a store in vuex from outside?

I have an iOS app that needs to pass data to a vue front-end:
const customerStore = new Vuex.Store({
state: {
data: [
{ id:1, title: 'Foo' },
{ id:2, title: 'Bar' }
]
},
mutations: {
list (state, data) {
state.data = data
}
}
})
const ListCustomersPage = {
key: 'ListCustomersPage',
template: '#ListCustomersPage',
components: { toolbar, cellcustomer },
data() {
return {
title: 'Select Customer',
items: customerStore.state.data
}
},
methods: {
push() {
}
}
};
However, I need to mutate the store from an injection on the webview:
web.InjectJavascriptAsync("customerStore.commit('list', [])").Start()
But the list is not changed. No error is shown when calling the injection.

Unable to hide nested tabNavigator's tabBar in react-navigation

I have a nested tabNavigator inside of another tabNavigator, and I'm trying to get the inner tabNavigator's tabBar to be hidden. (The one below that shows "Map" and "list"). I'd like to keep the main tabNavigator which reads "Map Favorites Add a Site More".
The code which is rendering it this way is below:
const MainNavigator = StackNavigator({
login: {
screen: LoginScreen
},
main: {
screen: TabNavigator({
search: {
screen: TabNavigator({
map: {
screen: MapScreen
},
list: {
screen: ListScreen
}
})
},
favorites: {screen: FavoritesScreen},
addSite: {screen: AddSiteScreen},
more: {screen: MoreScreen}
})
},
filter: {
screen: FilterScreen,
navigationOptions: {
tabBarVisible: false
}
}
}, {
lazy: true
});
I've tried adding
navigationOptions: {
tabBarVisible: false
}
to the search item as shown below:
search: {
screen: TabNavigator({
map: {
screen: MapScreen
},
list: {
screen: ListScreen
}
},
navigationOptions: {
tabBarVisible: false
})
}
but it ends up hiding the outermost TabNavigator (the one which reads "Map Favorites Add a Site More") instead of the inner one as I'd expect. (Image below)
Finally, out of ideas and thinking maybe it needs to be nested in as far as possible, I've also tried adding the
navigationOptions: {
tabBarVisible: false
}
to the innermost map and list items as such:
search: {
screen: TabNavigator({
map: {
screen: MapScreen,
navigationOptions: {
tabBarVisible: false
}
},
list: {
screen: ListScreen,
navigationOptions: {
tabBarVisible: false
}
}
})
}
but the result of this is hiding both of the TabNavigators and there are none at all. Am I missing something simple on how to achieve this?
Thanks
You are correct in adding the navigationOptions with a property of tabBarVisible: false. However, you want to add it as a property to the tabNavigator, not to the screen itself like so:
search: {
screen: TabNavigator({
map: {
screen: MapScreen,
navigationOptions: {
tabBarVisible: false
}
},
list: {
screen: ListScreen
}
}, navigationOptions: {
tabBarVisible: false
});
}
One thing I want to point out is that lazy: true was deprecated in react-navigation version 23, so unless you are on version 22 or below that won't do anything.
I never ended up finding a good answer to this question, and it seems like it might be a limitation of the library.
As a workaround (which works just as well), I decided instead of having three distinct screens: SearchScreen, MapScreen, and ListScreen, I just went down to only having one screen SearchScreen, and when that screen renders, it calls a component to render inside of it.
The navigation portion is now much flatter and looks like this:
main: {
screen: TabNavigator({
search: {screen: SearchScreen},
favorites: {screen: FavoritesScreen},
addSite: {screen: AddSiteScreen},
more: {screen: MoreScreen}
})
}
Then inside of the SearchScreen, I just render the components (instead of treating them as their own distinct screens)
renderSearchScreen = () => {
const {viewStyle, lastKnownRegion, mapLoaded, sites} = this.props;
if (viewStyle === map.SearchOptions.MAP) {
return (
<SearchMap
lastKnownRegion={lastKnownRegion}
mapLoaded={mapLoaded}
updateRegion={this.props.updateRegion}
sites={sites}
/>
);
} else {
return (
<SearchList
sites={sites}
/>
);
}
};
After these changes, it's worked fine with no issues. I suppose it's also cleaner, and more logical from an architecture standpoint that Map and List be components that are nested and rendered instead of them also being distinct screens.
Hopefully this can help anyone else who runs into a similar issue with nested TabNavigators!

Resources