React-native: How to get navigationOptions with Custom TabBarComponent - react-native

I'm new to react native and i'm trying to build a custom tab bar but i'm facing a problem when trying to display icons tab bar.
Here what i achieve so far.
Here my Custom TabBar component:
class TabBar extends Component {
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const isCapture = route.routeName === 'AddExpenses';
const focused = index === navigation.state.index;
const color = focused ? activeTintColor : inactiveTintColor;
if (isCapture === true) {
return (
<TouchableOpacity
key={route.key}
style={Styles.tab}
onPress={() => (navigation.navigate('AddExpensesModal'))}
>
<Ionicons
name={ioniconsByPlatform('add-circle')}
style={Styles.icon}
size={26}
/>
</TouchableOpacity>
);
}
return (
<TouchableWithoutFeedback
key={route.key}
style={Styles.tab}
onPress={() => (isCapture ? navigation.navigate('CaptureModal') : jumpToIndex(index))}
>
<View style={Styles.tab}>
<Text style={{ color }}>{route.routeName}</Text>
</View>
</TouchableWithoutFeedback>
);
}
render() {
const {
navigation,
} = this.props;
const {
routes,
} = navigation.state;
return (
<View style={Styles.tabBar}>
{routes && routes.map(this.renderItem)}
</View>
);
}
}
export default TabBar;
My Tab Navigator:
const MainTabNavigator = TabNavigator({
Summary: { screen: SummaryScreen },
AddExpenses: { screen: ExpensesScreen },
Expenses: { screen: ExpensesScreen },
}, {
tabBarComponent: TabBar,
});
export default MainTabNavigator;
And an example of a screen where i try to set my TabBarIcon:
const SummaryScreen = () => (
<View style={Styles.container}>
<Text>Summary</Text>
</View>
);
SummaryScreen.navigationOptions = {
title: 'Summary',
tabBarIcon: props => <TabBarIcon {...props} name="pulse" />,
};
export default SummaryScreen;
I want to be able to display my tab bar icons thanks to the navigationOptions property.
Do you have any idea how i can do this ?

If you feel TabNavigator is not powerful enough(which I think it's far from powerful), you could always customize a navigator view.
Here is my notes for customize a navigator view to replace TabNavigator:
export default class SectionTabView extends React.Component {
static propTypes = {
navigation: PropTypes.object
};
constructor(props) {
super(props);
}
render() {
const {router, navigation} = this.props;
const {routes, index} = navigation.state;
/**
* ActiveScreen is the current screen you see when you change you navigation state in tab bar
*/
const ActiveScreen = router.getComponentForState(navigation.state);
return (
<View style={Styles.section_container}>
<ActiveScreen
navigation={addNavigationHelpers({
...navigation,
state: routes[index],
})}
/>
<SectionTabBar navigation={navigation}/>
</View>
);
}
}
export default class SectionTabBar extends React.Component {
static propTypes = {
navigation: PropTypes.object
};
constructor(props) {
super(props);
}
getTabButtomGroupView() {
const {navigation} = this.props;
const {routes, index} = navigation.state;
let tabButtomGroupView = [];
routes.map((route) => {
let styles = [Styles.eventSection_tab];
const isClicked = routes[index].routeName === route.routeName;
if(isClicked){
styles.push(Styles.eventSection_tabClicked);
}
tabButtomGroupView.push(
<TouchableOpacity
onPress={() => {
/**
* when the routeName is equal to current routeName, we should stop navigate action
*/
if (routes[index].routeName === route.routeName) {
return;
}
navigation.navigate(route.routeName);
}}
style={styles}
key={route.routeName}>
<Text style={{color:'white'}}>{SectionRouteConfig[route.routeName].navigationOptions.title}</Text>
</TouchableOpacity>
)
});
return tabButtomGroupView;
}
render() {
return (
<View style={Styles.section_tabContainer}>
{this.getTabButtomGroupView()}
</View>
);
};
}
//SectionRouteConfig.js
export const sectionRouteConfig = {
XXX: {
screen: XXX, navigationOptions: {
title: XXX
}
},
XXX: {
screen: XXX, navigationOptions: {
title: XXX
}
}
};
export const SectionNavigator = createNavigator(TabRouter(sectionRouteConfig))(SectionTabView);
//Usage
render() {
const {dispatch, navigationState} = this.props;
return (
<SectionNavigator
navigation={
addNavigationHelpers({
dispatch: dispatch,
state: navigationState
})
}
/>
)
}
by the way I also use redux.
If those codes are too much for you , you can check the official example here:https://github.com/react-community/react-navigation/blob/master/examples/NavigationPlayground/js/CustomTabs.js

Related

TypeError : props.navigation.getParam is not a function. In(props.navigation.getParam('name')

I am facing issue on TypeError : props.navigation.getParam is not a function. In (props.navigation.getParam('name'). I am using reactNavigation version 5.x. this code is working in reactNavigation 3. What am I doing wrong?
Here is my code
export default class ChatScreen extends Component {
static navigationOption = ({ navigation }) => {
return {
title: navigation.getParam('name', null)
}
}
constructor(props) {
super(props);
this.state = {
person:{
name:props.navigation.getParam('name'),
phone:props.navigation.getParam('phone'),
// name:'Raushan',
// phone:9931428888
},
textMessage: ''
};
}
Error in state section value.
Stack navigator
`
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Auth">
<Stack.Screen name="AuthLoading" component={AuthLoadingScreen} />
<Stack.Screen name="App" component={HomeScreen} options={{ title: 'Chats' }}/>
<Stack.Screen name="Chat" component={ChatScreen} options={({ route }) => ({ title: route.params.name })}/>
<Stack.Screen name="Auth" component={LoginScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
`
and Navigate screen
onPress={()=>this.props.navigation.navigate('Chat',item)}
use props.route.params.name this is working
this.state = {
person:{
name: props.route.params.name,
phone: props.route.params.phone,
},
textMessage: ''
};
Mostly you are using the latest version of React-Navigation with the same old setup.
So, You gonna pass your custom param as the second argument in the navigation function that I pointed HERE:
// Index SCREEN:=>
const IndexScreen = ({ navigation: { navigate } }) => {
return (
<View style={Styles.container}>
<FlatList
data={state}
keyExtractor={(item) => `${item.id}`}
renderItem={({ item }) => (
<TouchableOpacity
style={{ ...Styles.singleBlog, ...Styles.rowing }}
onPress={() => navigate('Previewing', { id: item.id })} // HERE
>
<Text style={Styles.blogTitle}>{`${item.title}`}</Text>
</TouchableOpacity>
)}
/>
</View>
);
};
Then you will be able to extract the id wirth a new function called params
like so:
// PREVIEWING SCREEN:=>
const PreviewScreen = ({ route: { params } }) => {
const { state } = useContext(Context);
const { id } = params;
return (
<View style={Styles.container}>
<Text>preview post with id {id}</Text>
</View>
);
};
As of version 6.x you need to use route.
function Index({ navigation }) {
return (
<View>
<Button
title="Details"
onPress={() => {
navigation.navigate('Details', {
itemId: abcdef
});
}}
/>
</View>
);
}
function DetailsScreen({ route, navigation }) {
const { itemId } = route.params;
// console.log(itemId);
// abcdef
return (
// Content...
);
}
Source: https://reactnavigation.org/docs/params
Yes I found that in version2, 3, 4 of navigation using getParam.
Link is as follows: https://reactnavigation.org/docs/2.x/params/
when using version5 of navigation using props.navigation.route.param as per documentation.
https://reactnavigation.org/docs/params/ - version5.
In class component props can be access using this keyword. So try this :
export default class ChatScreen extends Component {
static navigationOption = ({ navigation }) => {
return {
title: navigation.getParam('name', null)
}
}
constructor(props) {
super(props);
this.state = {
person: {
name: this.props.navigation.getParam('name'), // access with this.
phone: this.props.navigation.getParam('phone'), //access with this.
// name:'Raushan',
// phone:9931428888
},
textMessage: ''
};
}
}

state params undefined using react-navigation

I can navigate to a screen but params are undefined, state is just:
{
key: "id-1574950261181-7",
routeName: "Video Player"
}
Navigate from:
render() {
const {
id,
title,
description,
video,
preview,
push,
dispatch,
testLink,
navigate
} = this.props;
return (
<TouchableHighlight
style={styles.episode}
activeOpacity={1}
underlayColor="#808080"
onPress={() => {
navigate("VideoPlayer", { tester: id });
}}
>
<View style={styles.title}>
<Text style={styles.text}>{title}</Text>
</View>
</TouchableHighlight>
);
}
My VideoPlayerScreen (for brevity) gives me :
import React from "react";
import {
...
} from "react-native";
...
...
class VideoPlayerScreen extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.onShare = this.onShare.bind(this);
this.navigateScreen = this.navigateScreen.bind(this);
this.bookmarkVideo = this.bookmarkVideo.bind(this);
this.loadRecapVideo = this.loadRecapVideo.bind(this);
}
....
render() {
const {
videos,
bookmarkVideo,
navigate,
state: {
params: { id }
}
} = this.props;
console.log(this.props.navigation.state.params);
let videoProps = videos.find(obj => obj.id == id);
return (<View />)
}
}
const mapDispatchToProps = dispatch => ({
bookmarkVideo: id => dispatch(bookmarkVideo(id))
});
const mapStateToProps = state => {
return {
videos: state.tcApp.videos
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(VideoPlayerScreen);
In case you are using react-native-navigation 4x you should change the props variable
from:
const {
...
navigate,
} = this.props;
to:
const {
...
navigation,
} = this.props;
and call it like:
onPress={() => {
navigation.navigate("VideoPlayer", { tester: id });
}}
Official Doc: React Native Navigation Docs
Friendly.

Need to show Expandable list view inside navigation drawer

I am an Android Application Developer. I have started working on React-Native. I am unable to find a way to show expandable list inside navigation drawer. Suggest a library if this functionality can be done in that. navigationOptions does not have a way to provide a list (refer code below).
I want to show expandable view like item 4
My Code is :-
import {DrawerNavigator} from 'react-navigation';
import React, {Component} from 'react';
import {
Platform,
StyleSheet,
Text,
Image,
View,
TouchableHighlight
} from 'react-native';
import Screen1 from './screen/Screen1'
import Screen2 from './screen/Screen2'
const util = require('util');
class MyHomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
headertitle: 'ffffff'
};
}
componentWillReceiveProps(nextProps) {
navigationOptions = {
title: this.nextProps.headertitle
};
}
static navigationOptions = {
drawerLabel: 'Home',
drawerIcon: ({tintColor}) => (
<Image
source={require('./images/document.png')}
style={[
styles.icon, {
tintColor: tintColor
}
]}/>),
title: 'NIIT'
};
render() {
return (<Screen1/>);
}
}
class MyNotificationsScreen extends React.Component {
static navigationOptions = {
drawerLabel: 'Notifications',
drawerIcon: ({tintColor}) => (<Image source={require('./images/smartphone.png')} style={[styles.icon]}/>),
title: 'Gnome'
};
render() {
return (<Screen2/>);
}
}
const styles = StyleSheet.create({
icon: {
width: 24,
height: 24
}
});
const DrawerScreen = DrawerNavigator({
Screen1: {
screen: MyHomeScreen
},
Screen2: {
screen: MyNotificationsScreen
}
}, {headerMode: 'none'})
export default DrawerScreen;
I think this is a single class, simple implementation of what the OP is asking for. It uses react-navigation v5. It's a standalone component that is configured via the ExpandableDrawerProps (title is the name of parent drawer, i.e. what contains the subdrawers and has no navigation, and choices is a map of label name to navigation screen component names.) It is written in TypeScript (both of these are .tsx files), so if you're not using TypeScript, just strip out the typing.
import {
DrawerContentComponentProps,
DrawerContentScrollView,
DrawerItem,
} from '#react-navigation/drawer';
import React from 'react';
import { Text, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import styles from './styles';
export type ExpandableDrawerProps = DrawerContentComponentProps & {
title: string;
choices: Map<string, string>;
};
export default class ExpandableDrawer extends React.Component<
ExpandableDrawerProps,
{
isExpanded: boolean;
}
> {
constructor(props: ExpandableDrawerProps, state: { isExpanded: boolean }) {
super(props);
this.state = state;
}
onPress = (): void => {
this.setState(() => {
return {
isExpanded: !this.state.isExpanded,
};
});
};
render = (): JSX.Element => {
return (
<View style={styles.container}>
<TouchableOpacity
activeOpacity={0.8}
onPress={this.onPress}
style={styles.heading}
>
<Text style={styles.expander}>{this.props.title}</Text>
</TouchableOpacity>
{this.state.isExpanded ? (
<DrawerContentScrollView>
<View style={styles.expandedItem}>
{[...this.props.choices.keys()].map(
(label: string): JSX.Element | null => {
const screen = this.props.choices.get(label);
if (screen != undefined) {
return (
<DrawerItem
key={label}
label={label}
onPress={(): void => {
this.props.navigation.navigate(screen);
}}
/>
);
} else {
return null;
}
}
)}
</View>
</DrawerContentScrollView>
) : null}
</View>
);
};
}
You can drop that code in a file, make a simple styles file or remove them from that code, and then you're able to use <ExpandableDrawerMenu {...expandable} />
in your normal drawer navigation.
Here's how I used it in a normal navigation drawer.
const DrawerContent = (props: DrawerContentComponentProps): JSX.Element => {
const c = new Map<string, string>();
c.set('SubItem 1', 'SubItem1');
c.set('SubItem 2', 'SubItem2');
const expandable: ExpandableDrawerProps = {
title: 'Expandable Drawer',
choices: c,
navigation: props.navigation,
state: props.state,
descriptors: props.descriptors,
progress: props.progress,
};
return (
<DrawerContentScrollView {...props}>
<View style={styles.drawerContent}>
<Drawer.Section style={styles.drawerSection}>
<DrawerItem
label="Item 1"
onPress={(): void => {
props.navigation.navigate('Item1');
}}
/>
<ExpandableDrawerMenu {...expandable} />
<DrawerItem>
label="Item 2"
onPress={(): void => {
props.navigation.navigate('Item2');
}}
/>
<DrawerItem
label="Item 3"
onPress={(): void => {
props.navigation.navigate('Item3');
}}
/>
</Drawer.Section>
</View>
</DrawerContentScrollView>
);
};
export default class Navigator extends Component {
render = (): JSX.Element => {
const Drawer = createDrawerNavigator();
return (
<NavigationContainer>
<Drawer.Navigator
drawerContent={(props: DrawerContentComponentProps): JSX.Element =>
DrawerContent(props)
}
initialRouteName="Item1"
>
<Drawer.Screen name="Item1" component={Item1Screen} />
<Drawer.Screen name="SubItem1" component={SubItem1Screen} />
<Drawer.Screen name="SubItem2" component={SubItem2Screen} />
<Drawer.Screen name="Item2" component={Item2Screen} />
<Drawer.Screen name="Item3" component={Item3Screen} />
</Drawer.Navigator>
</NavigationContainer>
);
};
}
react-navigation does not, at this time, support a collapsible menu in the drawer navigator.
You can, however, implement your own, by supplying your own contentComponent to the navigator:
const DrawerScreen = DrawerNavigator({
Screen1: {
screen: MyHomeScreen
},
Screen2: {
screen: MyNotificationsScreen
}
}, {
headerMode: 'none',
contentComponent: MyDrawer
})
const MyDrawer = (props) => ...
See the documentation for more information.
You can use something like react-native-collapsible to achieve the effect of the collapsible menu itself.
I have developed a solution for this problem. My code uses "#react-navigation/drawer": "^5.1.1" and "#react-navigation/native": "^5.0.9".
Gihub link - https://github.com/gyanani-harish/ReactNative-ExpandableDrawerMenu

How to call Screen / Component class method from react-navigation Header

I need to call SearchScreen class method from a React Navigation Header.
The Navigator look like this:
Search: {
screen: SearchScreen,
path: 'search/:query',
navigationOptions: {
title: 'Search',
header: {
right: (
<MaterialCommunityIcons
name="filter"
onPress={() => {
console.log(this);
}}
style={{marginRight: 15, color: 'white'}}
size={24}
/>
),
},
}
}
I've made it work by doing:
// declare static navigationOptions in the Component
static navigationOptions = {
title: 'Title',
header: ({ state }) => ({
right: (
<MaterialCommunityIcons
name="filter"
onPress={state.params.handleFilterPress}
style={{marginRight: 15, color: 'white'}}
size={24}
/>
),
}),
}
_handleFilterPress() {
// do something
}
componentDidMount() {
// set handler method with setParams
this.props.navigation.setParams({
handleFilterPress: this._handleFilterPress.bind(this)
});
}
I've resolved the issue the following way:
static navigationOptions = ({ navigation }) => {
return {
headerRight: () => (
<TouchableOpacity
onPress={navigation.getParam('onPressSyncButton')}>
<Text>Sync</Text>
</TouchableOpacity>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ onPressSyncButton: this._onPressSyncButton });
}
_onPressSyncButton = () => {
console.log("function called");
}
Hooks solution with FunctionComponent, useState and useEffect
Ref the official docs (https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component) it is done by:
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: <LogoTitle />,
headerRight: (
<Button
onPress={navigation.getParam('increaseCount')}
title="+1"
color="#fff"
/>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ increaseCount: this._increaseCount });
}
state = {
count: 0,
};
_increaseCount = () => {
this.setState({ count: this.state.count + 1 });
};
/* later in the render function we display the count */
}
However I could not get this to work when working with the hooks api. My state variables were always undefined, but after I thought about how the hooks api is implemented it all made sense, so the solution was to update the navigation param every time a significant state variable was changed:
const [count, setCount] = useState(0);
useEffect(() => {
props.navigation.setParams({ increaseCount });
}, [count]);
const increaseCount = () => setCount(count + 1);
I came across same issue and able to resolve the issue from below links.
class MyScreen extends React.Component {
static navigationOptions = {
header: {
right: <Button title={"Save"} onPress={() => this.saveDetails()} />
}
};
saveDetails() {
alert('Save Details');
}
render() {
return (
<View />
);
}
}
Source: react-native issues 145
Below is my code
import React, { Component } from "react";
import {
Container,
Header,
Item,
Input,
Icon,
Button,
Text,
Left,
Body,
Right,
Content,
Spinner,
List,
ListItem
} from "native-base";
import { View, Image, StyleSheet, Keyboard } from "react-native";
import { connect } from "react-redux";
import {
onClear,
onSearchTextChanged,
searchForProfiles
} from "../../actions/searchActions";
class SearchBar extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Header searchBar rounded>
<Button
iconLeft
style={{ paddingLeft: 0 }}
light
onPress={this.props.onBackPress}
>
<Icon style={{ marginLeft: 0, fontSize: 35 }} name="arrow-back" />
</Button>
<Item>
<Icon name="ios-search" />
<Input
placeholder="Search"
onChangeText={this.props.onChangeText}
value={this.props.searchText}
/>
<Button small transparent onPress={this.props.onClear}>
<Icon name="ios-close" />
</Button>
</Item>
<Button transparent onPress={this.props.onSearch}>
<Text>Search</Text>
</Button>
</Header>
);
}
}
class SearchWorld extends Component {
static navigationOptions = ({ navigation }) => ({
left: null,
header: () => {
const { state } = navigation;
return (
<SearchBar
onBackPress={() => navigation.goBack()}
onChangeText={text => {
state.params.onChangeText(text);
}}
onSearch={state.params.onSearch}
onClear={state.params.onClear}
searchText={state.params.searchText}
/>
);
}
});
onChangeText = text => {
this.props.navigation.setParams({
...this.props.navigation.state,
searchText: text
});
this.props.onSearchTextChanged(text);
};
onSearch = () => {
Keyboard.dismiss();
const profile = { search: "test" };
const token = this.props.token;
this.props.searchForProfiles(token, profile);
};
onClear = () => {
this.props.onClear();
this.props.navigation.setParams({
...this.props.navigation.state,
searchText: ""
});
};
componentDidMount() {
this.props.navigation.setParams({
onChangeText: this.onChangeText,
onSearch: this.onSearch,
onClear: this.onClear,
searchText: this.props.searchText
});
}
render() {
const { searchResults } = this.props;
let items = [];
if(searchResults && searchResults.data && searchResults.data.length > 0) {
items = [...searchResults.data];
}
return this.props.loading ? (
<Container style={{ alignItems: "center", justifyContent: "center" }}>
<Spinner color="#FE6320" />
</Container>
) : (
<Container>
<Content>
<List
style={{}}
dataArray={items}
renderRow={item => (
<ListItem style={{ marginLeft: 0}}>
<Text style={{paddingLeft: 10}}>{item.password}</Text>
</ListItem>
)}
/>
</Content>
</Container>
);
}
}
const mapStateToProps = state => {
const { token } = state.user;
const { searchText, searchResults, error, loading } = state.searchReaducer;
return {
token,
searchText,
searchResults,
error,
loading
};
};
export default connect(mapStateToProps, {
onClear,
onSearchTextChanged,
searchForProfiles
})(SearchWorld);
static navigationOptions = ({navigation}) => {
return {
headerTitle: () => <HeaderTitle />,
headerRight: () => (<Button iconLeft transparent small onPress = {navigation.getParam('onPressSyncButton')}>
<Icon style ={{color:'white', fontWeight:'bold'}} name='md-save' size = {32} />
<Text style ={{color:'white', fontWeight:'bold'}}>save</Text>
</Button>),
headerTintColor:'black',
headerStyle: {
backgroundColor: '#6200EE'
},
}
};
this.props.navigation.setParams({ onPressSyncButton: this.updateUserProfile });

Sending props with Tabs from 'react-native-tabs'

I am using Tabs from 'react-native-tabs' to navigate from one screen to another, how can I send props to the individual components of the tabs?
I am using the following code to display tabs,
import Tabs from 'react-native-tabs';
<Tabs
selected={page}
style={styles.tabbar}
selectedStyle={{color:'black'}}
onSelect={el=>this.setState({page:el.props.name})}
>
<Text style={styles.tabbarText} name={Contact}>Contact</Text>
<Text style={styles.tabbarText} name={Messages}>Messages</Text>
<Text style={styles.tabbarText} name={Profile}>Profile</Text>
</Tabs>
I figured it out, you can mention the props in the
<this.state.page style={styles.pageContainer} navigator={this.props.navigator}/>
line of code.
The entire render() function is here,
render() {
const { page } = this.state;
return (
<View style={styles.container}>
<this.state.page style={styles.pageContainer} navigator={this.props.navigator}/>
<Tabs
selected={page}
style={styles.tabbar}
selectedStyle={{color:'black'}}
onSelect={el=>this.setState({page:el.props.name})}
>
<Text style={styles.tabbarText} name={Contact}>Contact</Text>
<Text style={styles.tabbarText} name={Messages}>Messages</Text>
<Text style={styles.tabbarText} name={Profile}>Profile</Text>
</Tabs>
</View>
);
}
It may help you.
class Home2Screen extends React.Component {
render() {
console.log(this.props.navigation.state);
return (
<View>
</View>
);
}
}
class SettingsScreen extends React.Component {
render() {
console.log(this.props.navigation.state);
return (
<View>
</View>
);
}
}
const tab = TabNavigator(
{
Home2: { screen: Home2Screen },
Settings: { screen: SettingsScreen },
},
{}
);
class HomeScreen extends React.Component {
goToTab() {
this.props.navigation.navigate('Tab', {
carData: {some: 'data'},
});
}
render() {
return (
<View>
<Button onPress={() => { this.goToTab() }} />
</View>
);
}
}
const HomeNavigator = StackNavigator({
Home: {
screen: HomeScreen,
},
Tab: {
screen: tab,
},
}, {
mode: Platform.OS === 'ios' ? 'modal' : 'card',
});
const HomeNavigationDrawer = DrawerNavigator({
HomePage: {
screen: HomeNavigator,
},
}, {});
export default HomeNavigationDrawer;