Where is the `tabBarComponent` option in React-Navigation version 5? - react-native

I'm migrating a RN project version 4 to 5.
I was using the tabBarComponent option to replace tab bar component with a custom one. Old docs.
How to achieve the same in version 5, I can't find this option in the new docs.

Here is the new API way to achieve a custom tab component:
import { View, Text, TouchableOpacity } from 'react-native';
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1 }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
// ...
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
{...}
</Tab.Navigator>
This link will surely help.
https://reactnavigation.org/docs/en/next/bottom-tab-navigator.html#tabbar
Hope this helps. Cheers!

Related

react native reanimated animating text style

So, I was getting familiar with reanimated2, for that I'm building a simple todo app, so whenever I check a single todo (a simple toggle state, completed or !completed), I change the text style to line-through so that it appears to be completed, now in order to add some animations, I was trying to combine reanimated to it, but was unsuccessful, so can anyone guide me how to achieve what I want?
const TodoItem = ({ todo }) => {
const { toggleTodo } = useTodoStore((state) => state);
const progress = useSharedValue('none');
useEffect(() => {
progress.value = withTiming(todo.completed ? 'line-through' : 'none', {
duration: 1000,
easing: Easing.linear,
})
}, [todo.completed]);
const textReanimatedStyle = useAnimatedStyle(() => ({
textDecorationLine: progress.value, // ts error: Type 'string' is not assignable to type 'number | "none" | "underline" | "line-through" | "underline line-through" | undefined'.
}), []);
const toggleTodoHandler = () => {
toggleTodo(todo.id);
};
return (
<Animated.View
style={styles.container}
entering={FadeIn}
exiting={FadeOut}
layout={Layout.delay(300)}>
<View style={styles.innerContainer}>
<TouchableOpacity
style={styles.checkboxContainer}
activeOpacity={0.6}
onPress={toggleTodoHandler}>
{todo.completed ? (
<Ionicon name="checkmark" size={20} color={'gray'} />
) : null}
</TouchableOpacity>
<Animated.Text
numberOfLines={2}
style={[
styles.text,
textReanimatedStyle,
]}>
{todo.text}
</Animated.Text>
</View>
</Animated.View>
);
};
Now whenever I toggle complete or not, it throws error saying:
Invalid RCTTextDecorationLineType 'noneNaN'. should be one of: (
"line-through",
none,
underline,
"underline line-through"
)
Not sure this is what you wanted to achive. But what I did was can be handle with a state change also without reanimated.
There is an alternate way also. check this answer https://stackoverflow.com/a/65262789/8743951
const textState = useSharedValue('none');
const [todo, setTodo] = useState<{ text: string; completed: boolean }>({ text: 'complete this', completed: false });
const textReanimatedStyle = useAnimatedStyle(() => {
if (textState.value === 'checked') {
return {
textDecorationLine: 'line-through',
};
}
return {
textDecorationLine: 'none',
};
}, []);
const toggleTodoHandler = (): void => {
textState.value = todo.completed ? 'none' : 'checked';
setTodo({ ...todo, completed: !todo.completed });
};
return (
<Animated.View style={styles.container} entering={FadeIn} exiting={FadeOut} layout={Layout.delay(300)}>
<View style={styles.innerContainer}>
<TouchableOpacity style={styles.checkboxContainer} activeOpacity={0.6} onPress={toggleTodoHandler}>
{todo.completed ? <Ionicon name="checkmark" size={20} color="gray" /> : null}
</TouchableOpacity>
<Animated.Text numberOfLines={2} style={[styles.text, textReanimatedStyle]}>
{todo.text}
</Animated.Text>
</View>
</Animated.View>
);

create a customized scrollable top Tab bar using react-navigation-tab?

i need to create a customized scrollable top tab bar using react navigation tabs and tabBarComponent without using any other third party library.
const TopTabBar = createMaterialTopTabNavigator({
Home: HomePage,
Entertainment: EntertainmentNews,
Business: BusinessStack,
Music: MusicStack,
Politics: PoliticsStack,
Sports: SportsStack,
Technology: TechnologyStack,
WorldNews: WorldNewsStack
}, {
tabBarComponent: (props) => <TopTabBarComponent {...props}/>
})
In this with tab bar component i am able to create the top bar but it does not scroll when the screen are swiped ?
import React , { Component } from 'react'
import { Text, View, StyleSheet, FlatList, TouchableOpacity, Animated, Dimensions} from 'react-native'
interface Props {
navigation?: any
}
interface State {
//
}
export class TopTabBarComponent extends Component <Props, State>{
flatListRef
constructor(props: Props, state: State){
super(props, state)
}
onPressItem = (index) => {
const { navigation } = this.props
navigation.navigate( this.props.navigation.state.routes[index].routeName )
// this.onScrollIndex(index)
}
renderTopBar = (item, index) => {
const routes = this.props.navigation.state.routes
const activeIndex = this.props.navigation.state.index
return (
<TouchableOpacity style = {{
alignItems: 'center' ,
height: 50,
justifyContent: 'center',
borderBottomWidth: activeIndex === index ? 2 : 0,
borderColor: 'green',
paddingHorizontal: 5
}} onPress = {() => this.onPressItem(index)}>
<Text style = {{ fontSize: 20, color: 'blue'}}>{item.routeName}</Text>
</TouchableOpacity>
)
}
render() {
// reactotron.log('this.props', this.props.navigation)
console.warn('this.props.navigation.state.index', this.props.navigation.state.index)
return(
<View style = {{ marginHorizontal: 5}}>
<FlatList
initialScrollIndex = { this.props.navigation.state.index }
ref = {(ref) => { this.flatListRef = ref}}
// style = {{ paddingHorizontal: 20}}
horizontal
showsHorizontalScrollIndicator = {false}
data = {this.props.navigation.state.routes}
renderItem = {({item , index}) => this.renderTopBar(item, index) }
ItemSeparatorComponent = {() => <View style = {{ paddingRight: 40}}/>}
/>
</View>
)
}
}
This is top tab bar component code ? So how can i make the top tab scroll automatically when the screens are swiped ?
This is a sample to my tab bar that I quickly edited for your test case.
function MyTabBar({ state, descriptors, navigation, position }) {
const scrollViewRef = useRef(null)
useEffect(() => {
scrollViewRef.current.scrollTo({ x: state.index * 50, y: 0, animated: true })
}, [state.index])
return (
<View style={styles.tabContainer}>
<ScrollView ref={list => scrollViewRef.current = list} contentContainerStyle={{ flexDirection: 'row', alignItems: 'center'}} horizontal>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
let focusStyle = { }; //style object to append to the focussed tab
let tintColor = { };
let fontColor = { };
isFocused ? focusStyle = { backgroundColor: 'darkgray' } : focusStyle = { };
isFocused ? tintColor = { tintColor: 'black' } : tintColor = { tintColor: 'lightgray' }; // TODO: change this with proper fontColor codes
isFocused ? fontColor = { color: 'black' } : fontColor = { color: 'lightgray' };
//below controls the rendered tab
return (
<TouchableWithoutFeedback
key={index}
onPress={onPress}
onLongPress={onLongPress}
style={[styles.tabItem, focusStyle]}
>
<View style={styles.tabIconContainer}>
<LearnTabIcon title={route.name} color={tintColor} />
</View>
<Text style={[styles.labelStyle, fontColor]}>
{label}
</Text>
</TouchableWithoutFeedback>
);
})}
</ScrollView>
<View style={styles.tabBarIndicator}>
{ state.routes.map((route, index) => {
const isFocused = state.index === index;
let selectedStyle = { };
isFocused ? selectedStyle = { backgroundColor: 'darkgray'} : { }; // TODO: change this coloring to actual color codes
return (
<View
style={[styles.tabIndicators, selectedStyle]}
key={index}
>
</View>
);
})}
</View>
</View>
);
}
I'm using functional components for everything so it will look a bit different but in your custom tab bar grab the state.index on each index change and then use scrollToOffset to control where the scroll view is centered on screen. I put in a sample 50px offset scale just for simplicity.
I also wasn't using TypeScript but you seem to have most the boiler plate already ready to go anyway.
import React from 'react'
import {StatusBar} from 'react-native'
import {NavigationContainer} from '#react-navigation/native'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
import {createMaterialTopTabNavigator} from '#react-navigation/material-top-tabs'
import HomeScreen from './screens/HomeScreen'
import LivingRoom from './screens/LivingRoom'
import Fav from './screens/Fav'
import Settings from './screens/Settings'
const Tab = createMaterialTopTabNavigator()
const AppRoot = () => {
return (
<GestureHandlerRootView className="flex-1">
<StatusBar hidden />
<NavigationContainer>
<Tab.Navigator
screenOptions={{
tabBarScrollEnabled: true,
tabBarIndicator: () => null,
tabBarStyle: {
backgroundColor: '#000',
},
tabBarItemStyle: {
width: 'auto',
alignItems: 'flex-start',
},
tabBarLabelStyle: {
fontSize: 30,
fontFamily: 'Satoshi-Black',
color: '#fff',
textTransform: 'capitalize',
},
}}
sceneContainerStyle={{backgroundColor: '#000'}}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Living Room" component={LivingRoom} />
<Tab.Screen name="Fav" component={Fav} />
<Tab.Screen name="Settings" component={Settings} />
</Tab.Navigator>
</NavigationContainer>
</GestureHandlerRootView>
)
}
export default AppRoot

Custom Tab Bar React Navigation 5

I'm trying to make a tab bar like in the picture bellow, using React Navigation.
I tried some codes, but nothing worked. Some codes are from previous React Navigation Version. The really problem is make the tab bar have a margin from from bottom and both sides, and borders rounded.
Anyone can help me please?!
here is demo: https://snack.expo.io/#nomi9995/createbottomtabnavigator-%7C-react-navigation
you can use tabBar props to make custom TABBAR
<NavigationContainer>
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
MyTabBar Component
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row',backgroundColor:"#F4AF5F",height:50,borderRadius:50,justifyContent:"center",alignItems:"center" }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1, alignItems:"center" }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
The key to this is to add the style position: 'absolute' to the outer <View> container on your custom TabBar. This will get rid of the white background issue.

How to call Parent component function inside custom drawer

I am beginner in React Native. I am getting stuck in one issue. I have one Parent component Home.js in which there is Tab navigator on click of tab 3 child components replace based on selected key. In same page, I have custom drawer.Now, I want to change tabs on click of custom drawer's option & same thing when my 1st tab selected 1st option of drawer also set selected. How can i achieve this.
Here is my navigation Drawer :
export default MyDrawerNavigator = DrawerNavigator({
Page1: {
screen: props => <Home {...props} />,
}
},
{
contentComponent: props => (<CustomSideMenu {...props} />),
drawerWidth: (getScreenWidth() * 2.5) / 3,
}
);
Here is my Home class I want to access goToNextTab() inside Custom drawer
export class Home extends React.Component {
static navigationOptions = hidenavigation;
constructor(props) {
super(props);
}
apply_header = (val) => {
this.props.navigation.setParams({ Title: val });
}
goToNextTab = (tabName) => {
this.setState({ activeTab: tabName });
}
openDrawer() {
this.props.navigation.openDrawer();
}
tabs = [{
key: 'Dashboard',
icon: 'speedometer',
label: 'Dashboard',
pressColor: 'rgba(255, 255, 255, 0.16)'
},
{
key: 'Add Diamond',
icon: 'plus-circle-outline',
label: 'Add Diamond',
pressColor: 'rgba(255, 255, 255, 0.16)'
},
{
key: 'Diamond',
icon: 'diamond-stone',
label: 'Diamond',
pressColor: 'rgba(255, 255, 255, 0.16)'
}]
state = {
activeTab: 'Dashboard',
showFooter: true
};
renderIcon = icon => ({ isActive }) => (
<Icon size={24} color={isActive ? COLOR.action_bar : COLOR.tab_deselected_text_color} name={icon} />
)
renderTab = ({ tab, isActive }) => (
<FullTab isActive={isActive} key={tab.key} label={tab.label} labelStyle={isActive ? style.activeText : style.deactiveText} renderIcon={this.renderIcon(tab.icon)} />
)
render() {
const propsForChild = {
goToNextTab: (tabName) => this.goToNextTab(tabName),
openDrawer: () => this.openDrawer()
};
const propsForNav = {
nav: this.props,
openDrawer: () => this.openDrawer()
};
const addDimPropsForChild = {
openDrawer: () => this.openDrawer()
}
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
{
this.state.activeTab === 'Add Diamond' ? <Add_Dimond_Stack screenProps={addDimPropsForChild} /> : this.state.activeTab === 'Diamond' ? <Dimond_List_stack screenProps={propsForNav} /> : <Dashboard_Stack screenProps={propsForChild} />
}
</View>
{
this.state.showFooter ?
<BottomNavigation activeTab={this.state.activeTab} renderTab={this.renderTab} tabs={this.tabs} onTabPress={newTab => { this.setState({ activeTab: newTab.key }); }} />
: null
}
</View>
);
}
componentWillMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide.bind(this));
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow() {
//alert('Keyboard Shown');
this.setState({ showFooter: false })
}
_keyboardDidHide() {
//alert('Keyboard Hidden');
this.setState({ showFooter: true })
}
componentDidMount() {
printLogs('call', 'componentDidMount')
const { setParams } = this.props.navigation;
setParams({ myProps: 'test' });
}
}
Here is my Custom Drawer in which i want to access setSelectedPos() from Home tab click
export default class Custom_Side_Menu extends React.Component {
static navigationOptions = { hidenavigation };
state = {
current_selected: 0
}
setSelectedPos(pos) {
this.setState({ current_selected: pos });
}
closeNavigationPanel(pos) {
if (pos != 3) {
this.props.navigation.closeDrawer();
}
}
redirectToProfile() {
new NavigationRedirection().goToNextScreen('profile', this.props);
}
selectedColor(pos) {
if (this.state.current_selected === pos) {
return COLOR.input_text_color;
} else {
return COLOR.input_hint_color;
}
}
render() {
return (
<ScrollView>
<View style={stylePage.bg}>
{/* */}
<View style={{ flex: 1 }}>
<View style={{ padding: 10, alignContent: 'center', flexDirection: 'row', alignItems: 'center' }}>
<TouchableOpacity onPress={() => { this.closeNavigationPanel() }}>
<Icon name="arrow-left" size={30} color={COLOR.input_text_color} />
</TouchableOpacity>
<Text style={stylePage.menu_title}>Menu</Text>
</View>
<TouchableWithoutFeedback onPress={() => {
this.redirectToProfile();
}}>
<View>
<Image style={stylePage.profileImage} source={{ uri: 'https://uinames.com/api/photos/female/22.jpg' }} />
<Text style={stylePage.name}>Ruth McCoy</Text>
<Text style={stylePage.email}>ruth.mccoy#example.com</Text>
</View>
</TouchableWithoutFeedback>
<View style={stylePage.line_seprator} />
<View style={stylePage.menu_options}>
<Text style={[stylePage.menu_text, { color: this.selectedColor(0) }]} onPress={() => this.setCurrentSelection(0)}>Dashboard</Text>
<Text style={[stylePage.menu_text, { color: this.selectedColor(1) }]} onPress={() => this.setCurrentSelection(1)}>Diamonds List</Text>
<Text style={[stylePage.menu_text, { color: this.selectedColor(2) }]} onPress={() => this.setCurrentSelection(2)}>Add diamonds</Text>
<Text style={[stylePage.menu_text, { color: this.selectedColor(3) }]} onPress={() => this.setCurrentSelection(3)}>Profile</Text>
<Text style={[stylePage.menu_text, { color: this.selectedColor(4) }]} onPress={() => this.setCurrentSelection(4)}>Change Password</Text>
</View>
</View>
<TouchableOpacity style={{ alignSelf: 'baseline' }} onPress={() => clearAllData(this.props)}>
<View style={stylePage.logout_btn}>
<IconAnt name="logout" size={25} color={COLOR.white} />
<Text style={stylePage.logout_title}>Logout</Text>
</View>
</TouchableOpacity>
<RBSheet
closeOnDragDown={true}
closeOnPressMask={false}
ref={ref => { this.RBSheet = ref }}
height={getScreenHeight() / 2} duration={250} customStyles={{
container: { padding: 10, borderTopLeftRadius: 20, borderTopRightRadius: 20 },
}}>
<ChangePassword {...this.props} RBSheet={this.RBSheet} />
</RBSheet>
</View>
</ScrollView>
);
}
setCurrentSelection(pos) {
this.closeNavigationPanel(pos);
this.setSelectedPos(pos);
if (pos === 3) {
this.redirectToProfile();
} else if (pos === 4) {
this.RBSheet.open();
} else {
printLogs('props', this.props.navigation)
}
}
}
There are two problems.
Click on drawer options to change the navigation tab
On tab change set the option as active
using redux as global store
There is an easy way out if you need redux as your global store.
first connect your components with react-redux connect
manage the activeTab state in store instead of component state
then on click of drawer option change the state in redux for your activetab
this way you are able to solve the problem 1
Also make sure you check activetab from store if matched you can update the styling for active option in drawer . So here is solution for problem 2
Using tab navigator from react-navigation
another option is using tabNavigator from react-navigation itself that way you only need to call navigator function for changing tab
and getting the active tab from navigation state
* alternate to redux *
you can use react context apis for managing your parent state if you are not using redux

React Native Pass Index to Props

I have a modal that contain icons and description and status, and i want to pass the icons and descriptions from index to the modal,I already pass the status. is there anyway to do that? sorry i'm still new to react native and thanks in advance
this is my index.js
export const img =
{
itemStatus: {
"Open": { name: 'open-book', type: 'entypo', color: '#ffb732', desc:'New Attribut, New Attention'},
"Approved": { name: 'checklist', type: 'octicon', color: '#3CB371', desc:'Approved by SPV/MNG' },
"Escalated": { name: 'mail-forward', type: 'font-awesome', color: '#ffb732', desc:'Escalated to SPV/MNG' },
"Deliver Partial": { name: 'arrange-send-to-back', type: 'material-community', color: '#8B4513', desc:'Some items in a DO have not arrived/was faulty' },
};
and this is my container
class MyRequest extends React.Component {
constructor() {
super();
this.state = {
currentStatus: null,
refreshing: false,
fetchStatus: null
};
handleShowModal = (status) =>{
this.setState({
currentStatus: status,
});
}
handleDismissModal = () =>{
this.setState({currentStatus: null});
}
<View style={[styles.panelContainer, status === 'success' ? {} : { backgroundColor: color.white }]}>
<FlatList
showsVerticalScrollIndicator={false}
progressViewOffset={-10}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
onMomentumScrollEnd={(event) => event.nativeEvent.contentOffset.y === 0 ? this.onRefresh() : null}
data={content}
renderItem={({ item }) => item}
keyExtractor={(item, key) => key.toString()}
/>
</View>
<IconModal visible={this.state.modalVisible} close={this.handleDismissModal} icon={} status={this.state.currentStatus} desc={} />
}
and this is my modal
const IconModal = (props) => {
return(
<Modal
isVisible={props.visible}
onBackdropPress={props.close}
>
<View style={styles.dialogBox}>
<View style={styles.icon}>
<Icon>{props.icon}</Icon>
</View>
<View style={styles.text}>
<Text style={styles.status}>{props.status}</Text>
<Text>{props.desc}</Text>
</View>
<TouchableOpacity onPress={props.close}>
<View>
<Text style={styles.buttonText}>GOT IT</Text>
</View>
</TouchableOpacity>
</View>
</Modal>
)
}
It's a bit unclear how you plan on mapping against img.itemStatus index but you can just reference the object you want as such.
import img from '....path_to_index.js'
...
// const currentItemStatus = img.itemStatus.Open
// OR
const itemStatus = 'Open' // Or 'Approved', 'Escalated', 'Deliver Partial'
const currentItemStatus = img.itemStatus[itemStatus]
...
<IconModal
visible={this.state.modalVisible}
close={this.handleDismissModal}
icon={currentItemStatus.name} // Passing name
status={this.state.currentStatus}
desc={currentItemStatus.desc} // Passing desc
/>
...
Hope this was helpful