How to override/remove the transition when going back in React Navigation? - react-native

I'm creating a custom modal and would like to remove the back transition animation in React Navigation.
I have tried what is below, it removes the animation when I navigate to the modal component, but does not remove it when going back from the modal to the previous component.
const RootStack = createStackNavigator(
{ ... },
{
transitionConfig: () => ({
transitionSpec: {
duration: 0,
},
}),
}
);
So when I go back, there's a slideDown animation and I expect there to be none. I have only tested it on Android. Any help would be appreciated :)

Related

How do we implement Scroll to top functionality on tapping of the corresponding tab button on a bottom tab navigator in react navigation?

The React Navigation version I am using is v5. In the ScrollView corresponding to a Bottom Tab Icon, if the user is already on that given screen, I want to enable functionality where the user scrolls to the top when this icon is pressed.
As stated in the documentation, this feature should be already implemented. But I think you have placed your ScrollView inside a nested StackNavigator, right?
In that case, you probably need to subscribe to TabNavigator event and fire your scrollToTop manually
React.useEffect(() => {
const unsubscribe = navigation.addListener('tabPress', e => {
// Get your scrollView ref and dispatch scrollToTop
});
return unsubscribe;
}, [navigation]);
Hope it'll help you!
None of the solutions worked for me on the web, not using listener and not using useScrollToTop which is provided by react-navigation. Because I have nested stacks and navigators and also custom tabbar.
I solved the problem by setting a navigation param for scrolling to the top.
I have custom tabbar, and I needed to scroll to top on the homepage which was the first route of the tab stack.
So, in the homepage I set the scrollToTop parameter for the navigator:
const homelistRef = React.useRef<FlatList | null>(null)
useFocusEffect(
useCallback(() => {
navigation.setParams({
scrollToTop: () => homelistRef.current?.scrollToOffset({ offset: 0, animated: true }),
});
}, [navigation, homelistRef]),
);
return (
<FlatList
ref={homelistRef}
...{other Flatlist configs}
/>
)
And in my Tabbar component, I read this new param and execute it in the onPress function of the tabs:
interface IProps extends BottomTabBarProps {}
const TabBar: React.FC<IProps> = ({ state, navigation }) => {
const handleTabPress = useCallback(
(route) => async () => {
const currentRoute = state.routes?.[state.index];
if (
route === currentRoute?.name &&
state.routes?.[0].state?.routes?.[0]?.params?.scrollToTop
) {
state.routes[0].state.routes[0].params.scrollToTop();
}
},
[navigation, state],
);
return (
{render tab component}
)
}
And that's the ultimate solution for executing some action on active tabs.

undefined is not an object (evaluating 'navigation.navigate')

I am trying to navigate from one screen to other screen inside tabbar in react native.
But, I am getting following error
ButtonClickCheckFunction = () => {
const { navigation } = this.props;
navigation.navigate('detailsScreen', { detailsScreen: jsonData });
}
Any suggestions?
For main screens, In tab bar we have created stack,
const AppStack = createAppContainer(createDrawerNavigator({
Dashboard: {
screen: ProfileStack,
},
Connect: {
screen: Connect,
},
screen1: {
screen: Screen1,
}
});
But, In Dashboard screen we are showing tabbar. I am working with tab2. So, From tab2, I have details screen. In that details screen I have to show navigation bar with back button arrow like custom image.
So, How to fix this?
If you post more code, we'll be able to better answer your question.
From the error message though, it seems like you're not properly creating the component so that the navigation property is set. You'll need to make sure to wrap the component using the withNavigation higher order component.
export default withNavigation(MyComponent);

How to selectively show modals in React Navigation?

I'm building an app that is very similar to the Calendar app on iOS. I have a tab-based app that should sometimes trigger modals and other times cards, based on the screen being shown. In Apple's Calendar app, if you click on an event, you'll get a normal (slide from side or "card") transition, but if you click on the "+" button, you'll get a modal (bottom up) transition. I'd like to achieve something similar. The problem now is I can only get either all modals or all card transitions.
const CalendarTab = createStackNavigator(
{
CalendarView: {screen: CalendarView},
ViewEvent: {screen: ViewEvent}, // This should be a "card" transition
AddEvent: {screen: AddEvent}, // This should be a "modal" transition
},
{
mode: "modal", // This shouldn't be hard-coded
initialRouteName: "CalendarView",
}
);
const TabStack = createBottomTabNavigator({
Calendar: CalendarTab,
Settings: SettingsTab,
},
{
initialRouteName: "Calendar",
navigationOptions: ({ navigation }) => ({
tabBarVisible: navigation.state.index === 0, // This should only apply to modals
});
}
export default class App extends Component {
render() {
return <TabStack />;
}
}
Edit: I did find a partial solution here: https://github.com/react-navigation/react-navigation/issues/707#issuecomment-299859578, but it causes the headers to get messed up and is generally a brittle solution.

how to destroy a screen after navigating from it in StackNavigator

Using react navigation. I have a StackNavigator
const Stack = StackNavigator( // eslint-disable-line new-cap
{
List: {
screen: DrugPage,
},
Create: {
screen: DrugCreate,
},
},
{
initialRouteName: 'List',
}
);
The first screen is a list of entities and the second screen is to create a new entity that will add to the list. The first List screen has a nice link to 'Add Entity' in the navigation bar which goes to the Create route. After creating the entity I use navigation.navigate to go back to the List route. This leaves the create entity screen on the stack and so then a back button appears in the nav bar on my list screen. I don't want the Create Entity screen to remain in the stack after the entity is successfully created--I want it destroyed so Create screens don't build up in a stack that I don't need and so I don't have a back button I don't want on the List screen. I thought about using a StackNavigator but that doesn't give you a nice navbar at the top (in iOS). Any recommendations?
I had a problem very similar to yours, and after days searching I was able to solve my problem by adding a line of code, which in my case destroys the screen as soon as it leaves it.
add this to the properties of drawerNavigator : unmountInactiveRoutes: true
follows an example of my code :
const drawMenu = createDrawerNavigator({
StackHome,
StackOS
},{
unmountInactiveRoutes: true,
initialRouteName: 'StackHome'
}
);
I used the reset action in NavigationActions per #Pritish Vaidya's comment on my original question. (https://reactnavigation.org/docs/navigation-actions#reset)
Implementation
const resetAction = NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({routeName: 'List'})],
key: null,
});
navigation.dispatch(resetAction);
https://reactnavigation.org/docs/navigation-actions#reset
Based on the answers above, with little improvements this is what worked for me:
import { CommonActions } from '#react-navigation/native';
const SplashScreen = () => {
const isFocused = useIsFocused();
const navigation = useNavigation();
...
useEffect(() => {
if (!isFocused) {
navigation.dispatch(state => {
const routes = state.routes.filter(item => item.name !== 'SplashScreen');
return CommonActions.reset({ ...state, routes, index: routes.length - 1 });
});
}
}, [isFocused, navigation]);
...
return ...
}
The easiest way is to use pop(), then you navigate to main screen
navigation.pop();
navigation.navigate('List')

How do i make a TabNavigator button push a modal screen with React Navigation

Using the React Navigation tab navigator https://reactnavigation.org/docs/navigators/tab how do I make one of the tab buttons push the screen up as a full screen modal? I see the stack navigator has a mode=modal option. how do I get that mode to be used when clicking on the TakePhoto tab button? Clicking on it currently still shows the tab bar on the bottom.
const MyApp = TabNavigator({
Home: {
screen: MyHomeScreen,
},
TakePhoto: {
screen: PhotoPickerScreen, // how can I have this screen show up as a full screen modal?
},
});
Actually, there is no support in react-navigation to change the way of presentation on the fly from default to modal (see the discussion about this here). I ran into the same issue and solved it by using a very top StackNavigator with headerMode set to none and mode set to modal:
const MainTabNavigator = TabNavigator(
{
Tab1Home: { screen: Tab1Screen },
Tab2Home: { screen: Tab2Screen }
}
);
const LoginRegisterStackNavigator = StackNavigator({
Login: { screen: LoginScreen }
});
const ModalStackNavigator = StackNavigator({
MainTabNavigator: { screen: MainTabNavigator },
LoginScreenStackNavigator: { screen: LoginRegisterStackNavigator }
}, {
headerMode: 'none',
mode: 'modal'
});
This allows me to do the following (using redux) in Tab1Screen and Tab2Screen to bring up the modal view from wherever I want:
this.props.navigation.navigate('LoginScreenStackNavigator');
Not sure if this is still relevant for you, but i've managed to find away to achieve this.
So i've managed to get it working by using the tabBarComponent inside the tabNavigatorConifg, you can stop the tab navigation from navigating depending on the index.
tabBarComponent: ({jumpToIndex, ...props, navigation}) => (
<TabBarBottom
{...props}
jumpToIndex={index => {
if (index === 2) {
navigation.navigate('camera')
}
else {
jumpToIndex(index)
}
}}
/>
)
Once you've done this, my method of showing the view modally on top of the tab views was to put the tabnavigator inside of a stacknavigatior and then just navigate to a new screen inside of the stacknavigator.
react-navigation's bottomTabNavigator has a tabBarOnPress navigation option you can use to override tab presses:
https://reactnavigation.org/docs/en/bottom-tab-navigator.html#tabbaronpress
const AppContainer = createStackNavigator(
{
default: createBottomTabNavigator(
{
TAB_0: Stack0,
TAB_1: Stack1,
TAB_2: Stack2,
TAB_3: View // plain rn-view, or any old unused screen
},
{
defaultNavigationOptions: {
// other tab navigation options...
tabBarOnPress: ({ navigation, defaultHandler }) => {
if (navigation.state.key === 'TAB_3') {
navigation.navigate('tabToOpenAsModal');
} else {
defaultHandler();
}
}
}
}
),
tabToOpenAsModal: {
screen: TabToOpenAsModalScreen
}
},
{
mode: 'modal',
headerMode: 'none'
}
);
If you nest your tab navigator within a stack navigator with a modal, you can open this when the tab bar button is pressed. Since the modal was opened and not the tab, when the modal is closed the app returns to the screen that was visible before the modal tab was pressed.
One way to make the tab bar go away is to hide the tabBar with visible: false:
const MyApp = TabNavigator({
Home: {
screen: MyHomeScreen,
},
TakePhoto: {
screen: PhotoPickerScreen,
navigationOptions: {
tabBar: {
visible: false,
},
},
},
});
However, that does not seem to trigger any transition to fullscreen, which I guess is desired?
Another option could be to wrap PhotoPickerScreen inside a new StackNavigator and set that navigator to mode='modal'.
You might have to trigger the navigation to that modal from onPress on the tabItem somehow (eg. navigation.navigate('TakePhoto').)
Note, I'm trying to wrap my head around how best to structure navigation myself, so …
Third option, implementing a StackNavigator as parent, then adding the MyApp TabNavigator as the first route inside of it, could be the most flexible solution. Then the TakePhoto screen would be on the same level as the TabNavigator, allowing you to route to it from wherever.
Interested to hear what you come up with!
I suggest two solution
First one is about hide it
For seconde one please read that: https://reactnavigation.org/docs/hiding-tabbar-in-screens
<Tab.Screen
name={Routes.CREATE_ANNOUNCEMENT_SCREEN}
// name={Routes.TEST_SCREEN}
options={{
.
.
.
tabBarVisible: false, <----- solution 1
tabBarButton: (props) => ( <----- or solution 2
<TouchableOpacity
{...props}
onPress={() => {
navigation.navigate(Routes.DETAILS_SCREEN);
}}
/>
),
}}
component={CreateAnnouncementScreen}
/>
Docs are patchy in some areas, but here it is:
export default class HomeScene extends Component {
static navigationOptions = {
title: 'foo',
header:{
visible: true
}
}
....
}