Tab bar navigation without component screen render - react-native

Is there a way to press on tab bar and not render component screen? i have been passing null function but it still renders blank screen. i want that wherever you press it stays on home screen.

Add a listener to the Screen.
component={() => null}
listeners={() => ({
tabPress: (e) => {
e.preventDefault(); // Prevents navigation
// Your code here for when you press the tab
},
})}

You can avoid navigation to the screen by creating your custom tabBar component in which you can handle onPress to each tabBarComponent so you can avoid navigation and do some action instead. Take a closer look at the example from react-navigation documentation here:
https://reactnavigation.org/docs/bottom-tab-navigator/#tabbar
This line of code from the example should be helpful for you:
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};

Related

How do I update react-native component, when click on tab on bottom Tab Navigator

I am using bottom tab navigator in React-native for Navigation. When I switches tab, component are not updating.
Pls let me know how can I update/refresh whole component when I tap on tab at bottom Tab Navigator
Here is a simple solution.
import { useFocusEffect } from '#react-navigation/native';
useFocusEffect(
React.useCallback(() => {
console.log("Function Call on TAb change")
}, [])
);
Here is the link you can read more. https://reactnavigation.org/docs/function-after-focusing-screen/
You can use Navigation listener check Navigation Events, when screen gets focused it will trigger a function like this:
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
//Your refresh code gets here
});
return () => {
unsubscribe();
};
}, [navigation]);
And class component like this:
componentDidMount() {
this._unsubscribe = navigation.addListener('focus', () => {
//Your refresh code gets here
});
}
componentWillUnmount() {
this._unsubscribe();
}
If you want to force update check this question

React-Native navigation jumps to wrong screen with useEffect and setTimeout

I have a navigation stack with different screens that I wan't to render after each other. I set the timeout to be 5 seconds, so it will go to the next screen in the stack.
<Stack.Screen name="StoryScreenOne" component={StoryScreenOne} />
<Stack.Screen name="StoryScreenTwo" component={StoryScreenTwo} />
<Stack.Screen name="StoryScreenThree" component={StoryScreenThree} />
My timeout function is this
useEffect(() => {
setTimeout(() => {
props.navigation.navigate('StoryScreenTwo');
}, 5000);
});
The problem is that if I am at screen two and navigate back to screen one, the next screen that renders is screen three, not screen two as I want it to be.
Any tips for this?
If i understand when you arrive on the screen two there's an other useEffect like this:
useEffect(() => {
setTimeout(() => {
props.navigation.navigate('StoryScreenThree');
}, 5000);
});
I think the setTimeout is still running so if you want to rerender screen 2 on goBack you need to use a clearTimeOut when you press the back button.
Look a this : https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals
But your first screen will not unmount when you go navigate so you need to use a listener to relaunch the UseEffectlike this :
import { useFocusEffect } from '#react-navigation/native';
function Profile() {
useFocusEffect(
React.useCallback(() => {
// Do something when the screen is focused
return () => {
// Do something when the screen is unfocused
// Useful for cleanup functions
};
}, [])
);
return <ProfileContent />;
}
more info here : https://reactnavigation.org/docs/navigation-lifecycle/

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.

Differentiate between swipe and click navigation in tabs in react-navigation

To better understand how our users is using our app, we want to fire an event to analytics when a user swipe to the next tab, and a different event if the user clicks to navigate to the next tab.
The tabs are created with createMaterialTopTabNavigator from react-navigation-tabs.
const SwipeableTabs = createMaterialTopTabNavigator(
{
Tab1,
Tab2,
Tab3,
},
{
swipeEnabled: true,
}
);
I've tried listening to the following event, but the payload does not contain any information about how the user navigated (swipe vs click).
this.props.navigation.addListener('didFocus', payload => console.log(payload))
Is there any way to know if the user swiped or clicked, so that I can fire the appropriate event to analytics?
You could try adding a press listener to the tab bar as described here.
The demo code looks like this:
const MyTabs = TabNavigator({
...
}, {
tabBarComponent: TabBarBottom /* or TabBarTop */,
tabBarPosition: 'bottom' /* or 'top' */,
navigationOptions: ({ navigation }) => ({
tabBarOnPress: (scene, jumpToIndex) => {
console.log('onPress:', scene.route);
jumpToIndex(scene.index);
},
}),
});
Now you can simply set a flag in your tabBarOnPress function and check for it in your didFocus listener. If the flag has been set then you know it was via tab bar press, otherwise it must be a swipe. Note that this assumes you don't manually set the tab on your own somewhere. But if you do, you could just set the flag in that press handler as well.
tabBarOnPress docs
There is listeners on screens, you can use swipeStart, swipeEnd or tabPress callbacks to fire your events to the analytics.
<Tab.Screen
name="Chat"
component={Chat}
listeners={({navigation, route}) => ({
tabPress: (e) => {
// call your analytics events
},
swipeStart: (e) => {
// call your analytics events
},
swipeEnd: (e) => {
// call your analytics events
},
})}
/>

react-native-navigation: how to hide bottomTabs when keyboard is shown?

I am using the bottomTabs navigator from react-native-navigaton to navigation within my app. However, each time I have for example a TextInput field, the bottomTabs are being pushed up.
What is a possible way to hide the bottomTabs whenever the keyboard is being shown?
just add tabBarHideOnKeyboard: true to screenOptions
Add the following to your android manifest in android/app/src/AndroidManifest.xml
in your activity tag add/replace this attribute
the bottom tab should be hidden now.
android:windowSoftInputMode="stateUnspecified|adjustPan"
You can programmatically hide the bottom tab when the keyboard is open using React hooks
const _keyboardDidShow = useCallback(() => {
navigation.setOptions({
tabBarVisible: false,
});
}, [navigation]);
const _keyboardDidHide = useCallback(() => {
navigation.setOptions({
tabBarVisible: true,
});
}, [navigation]);
useEffect(() => {
Keyboard.addListener('keyboardDidShow', _keyboardDidShow);
Keyboard.addListener('keyboardDidHide', _keyboardDidHide);
// cleanup function
return () => {
Keyboard.removeListener('keyboardDidShow', _keyboardDidShow);
Keyboard.removeListener('keyboardDidHide', _keyboardDidHide);
};
}, [_keyboardDidHide, _keyboardDidShow]);
Answer for React Navigation V6 with or without a Custom tabBar
I'm using react navigation V6 since i'm using a custom tabBar the tabBarHideOnKeyboard: true prop not working but when i change the custom tabBar to default tab bar that prop works but i don't like the behavior of that prop on android, so i used keyboard from react-native to check if the keyboard is active or not and setting the css display prop to 'none'
import { Keyboard, View } from "react-native";
const [keyboardStatus, setKeyboardStatus] = useState<boolean>();
useEffect(() => {
const showSubscription = Keyboard.addListener('keyboardDidShow', () => {
setKeyboardStatus(true);
});
const hideSubscription = Keyboard.addListener('keyboardDidHide', () => {
setKeyboardStatus(false);
});
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, []);
so on the custom tab bar component now we can do like this.
<View style={
[
styles.mainContainer,
keyboardStatus ? styles.hideTabNavigation : null,
]
}
>
// your code here
</View>
Hope this will help someone until they fix this issue!