How to make navigation header title dynamic in React-native? - javascript

In my React-native project, I am using drawer navigation. I have set the some screen inside the Drawer navigation and some of the screens outside the Drawer navigation.
I have declared a class named HomeDrawer, for this set up. Here's the code of that class.
HomeDrawer.js
class NavigationDrawerStructure extends React.Component {
constructor() {
super();
this.state = {
ModalVisibleStatus: false,
}
};
static navigationOptions = {
header: null ,
};
toggleDrawer = () => {
this.props.navigationProps.toggleDrawer();
};
render() {
return (
<View style={{ flexDirection: 'row', marginLeft:5 }}>
<TouchableOpacity onPress={this.toggleDrawer.bind(this)}>
<Image style={{width:20, height:20, margin:5, justifyContent:'center'}} source={require('../assets/menu.png')} />
</TouchableOpacity>
</View>
);
}
}
const FirstActivity_StackNavigator = createStackNavigator({
First: {
screen: HomeScreen,
navigationOptions: ({ navigation }) => ({
title: 'Home',
headerLeft: <NavigationDrawerStructure navigationProps={navigation} />
),
headerStyle: {
backgroundColor: '#FF9800',
},
headerTintColor: '#fff',
}),
},
ScreenInternal: {
screen: BookDetails,
navigationOptions: ({ navigation }) => ({
title: 'Screen With Navigation Drawer',
headerLeft: <NavigationDrawerStructure navigationProps={navigation} />,
headerStyle: {
backgroundColor: '#FF9800',
},
headerTintColor: '#fff',
}),
},
});
const Screen2_StackNavigator = createStackNavigator({
Second: {
screen: CategoryBrowse,
navigationOptions: ({ navigation }) => ({
title: 'Create Note',
headerLeft: <NavigationDrawerStructure navigationProps={navigation} />,
headerStyle: {
backgroundColor: '#FF9800',
},
headerTintColor: '#fff',
}),
},
});
const Drawer = createDrawerNavigator({
Screen1: {
screen: FirstActivity_StackNavigator,
navigationOptions: {
drawerLabel: 'Home',
drawerIcon: (
<Icon name='home' size={25}
/>
)
},
},
Screen2: {
screen: Screen2_StackNavigator,
navigationOptions: {
drawerLabel: 'Category',
drawerIcon: (
<Image source={require('../assets/splash.png')}
style={{height:25, width:25,}}
/>
)
},
},
},
{
contentComponent: CustomSidebarMenu,
//Sidebar width
drawerWidth: Dimensions.get('window').width - 130,
},
);
const Drawer2 = createDrawerNavigator({
Screen1: {
screen: FirstActivity_StackNavigator,
navigationOptions: {
drawerLabel: 'Home',
drawerIcon: (
<Icon name='home' size={25}
/>
)
},
},
Screen2: {
screen: Screen2_StackNavigator,
navigationOptions: {
drawerLabel: 'Category',
drawerIcon: (
<Image source={require('../assets/splash.png')}
style={{height:25, width:25,}}
/>
)
},
},
},
{
drawerType: 'back',
drawerPosition: 'right',
drawerWidth: 200,
drawerBackgroundColor: 'orange',
//For the Custom sidebar menu we have to provide our CustomSidebarMenu
contentComponent: CustomSidebarMenu,
//Sidebar width
drawerWidth: Dimensions.get('window').width - 130,
},
);
const DrawerNavigatorExample = createStackNavigator({
Drawer: { screen: Drawer, navigationOptions: { header: null } },
BookDetails: {
screen: BookDetails,
navigationOptions: { title: 'Book Details' },
},
CartPage: {
screen: CartPage,
navigationOptions: { title: 'Cart Page' },
},
},
);
In the above code, you can see that I have an internal screen named 'HomeScreen' and one external screen of drawer named 'BookDetails'. I have also set the navigationOptions title for the screens.
Now, in the HomeScreen I have a click event , by pressing on e element I pass a value and start the external screen 'BookDetails'. Here's the code for that-
<TouchableOpacity style={{flexDirection:'row'}}
onPress={() => this.props.navigation.navigate('BookDetails', {
JSON_ListView_Clicked_Item: item.id,
})}
>
Now, in the BookDetails screen I want to change the navigation header title according to the value I receive from HomeScreen.
Here's my code of the BookDetails Screen-
BookDetails.js
class BookDetails extends Component {
constructor() {
super()
this.state= {
dataSource : [],
isLoading: true,
bookId: '',
responseDetails:'',
loading: true
}
}
async componentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener("didFocus", () => {
this.getBookDetailsApi();
});
this.setState({
bookId:(
this.props.navigation.state.params.JSON_ListView_Clicked_Item
? this.props.navigation.state.params.JSON_ListView_Clicked_Item
: 'No Value Passed'
)
})
}
async componentWillMount() {
await Font.loadAsync ({
Roboto: require('native-base/Fonts/Roboto.ttf'),
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf')
});
this.setState({loading:false});
}
getBookDetailsApi() {
const url = 'my url';
return fetch(url, {
method: 'GET',
headers: new Headers({
'Content-Type' : 'application/json',
'app_api_key': APP_API_KEY,
})
})
.then((response)=> response.json() )
.then((responseJson) => {
console.log('####:'+responseJson.nameEnglish)
this.setState({
responseDetails: responseJson,
isLoading: false,
})
})
.catch((Error) => {
console.log(Error)
});
};
}
render() {
if(this.state.loading) {
return(
<Root>
<AppLoading/>
</Root>
);
}
return (
<View style={styles.container}>
<TouchableOpacity
onPress = {()=>{
this.props.navigation.navigate('CartPage')
}}
>
<Text style={styles.title}>Hello Splash</Text>
</TouchableOpacity>
</View>
);
}
}
export default BookDetails;
Now, I need a solution to keep the BookDetails Screen navigation header title dynamic and keep changing it according to the values I receive from HomeScreen. So, it would be very nice if someone give a solution to that.

Related

How can I hide menu from drawer navigation in react native base on database user permission?

I am trying to make a project in react native using expo.
I have a switch navigation for login. After login I have a drawer navigation that contains separate stack navigation. For each screen I want to hide few menus from drawer navigation based on user permissions as the application will be used by admin, employee, manager etc.
Or is there any other way to create a dynamic drawer navigation based on my requirement with menu fetched from database according to user permission?
Drawer navigation should be rendered based on user type after login.
Here is my code snippet for drawer navigation:
const DrawerNav = createDrawerNavigator({
HomeScreen: {
screen: createStackNavigator({
HomeScreen: {
screen: HomeScreen,
navigationOptions: ({ navigation }) => {
return {
title: "Dashboard",
headerLeft: (
<Ionicons
name="md-menu"
size={32}
color="white"
style={{ paddingLeft: 20 }}
onPress={() => navigation.toggleDrawer()}
/>
),
headerStyle: {
backgroundColor: "#B00020"
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "bold"
}
};
}
}
}),
navigationOptions: ({ navigation }) => ({
drawerLabel: "Dashboard",
drawerIcon: () => <Ionicons name="md-home" size={28} color="#B00020" />
})
},
UserlistDetails: {
name: UserlistDetails,
screen: createStackNavigator({
UserlistDetails: {
screen: UserlistDetails,
navigationOptions: ({ navigation }) => {
return {
title: "User Accnt Details",
headerLeft: (
<Ionicons
name="md-menu"
size={32}
color="white"
style={{ paddingLeft: 20 }}
onPress={() => navigation.toggleDrawer()}
/>
),
headerStyle: {
backgroundColor: "#B00020"
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "bold"
}
};
}
},
CreateNewUser: {
screen: CreateNewUser,
navigationOptions: ({ navigation }) => {
return {
title: "Create New User",
headerStyle: { backgroundColor: "#B00020" },
headerTintColor: "#fff",
headerTitleStyle: { fontWeight: "bold" }
};
}
}
}),
navigationOptions: ({ navigation }) => ({
drawerLabel: "User Accnt Details",
drawerIcon: () => <Ionicons name="md-person" size={28} color="#B00020" />
})
}
});
const StackNav = createStackNavigator(
{ Dashboard: DrawerNav },
{
defaultNavigationOptions: ({ navigation }) => {
return { header: null };
}
}
);
const Navigation = createAppContainer(StackNav);
export default class Home extends React.Component {
static navigationOptions = { header: null };
render() {
return (
<View style={styles.container}>
<Navigation />
</View>
);
}
}
If you check the react navigation docs, it states that one limitation is the lack of ability to have dynamic routes. All routes have to be static and defined at the beginning. See more here:
https://reactnavigation.org/docs/en/limitations.html#targetText=Dynamic%20routes&targetText=React%20Navigation%20does%20not%20currently,can%20do%20this%20using%20params.

How can 'this.state.userName' in constructor can be fetched from 'contentComponent' to be placed in a header section of a drawer in react-navigation?

I have a variable 'this.state.userName' in a constructor obtained from AsyncStorage which log perfect at constructor. I want it to be rendered in the header of navigation drawer of react-navigation. I am so mush messed up with the flow as i am new to react-native. I already wasted entire day. The result on the header is null or no any text is shown , No any error too.
The callbacks of setState of 'this.setState.userName':
06-15 00:40:22.211 20510 29463 I ReactNativeJS: { userName: 'Ramesh mike' }
I have tried the following structure:
class ScreensSetup extends Component {
toggleDrawer = () => {
this.props.navigationProps.toggleDrawer();
};
constructor(props) {
super(props);
AsyncStorage.getItem('KeyUserName').then(value =>{
this.setState({ userName: value}, () => console.log(this.state) );
});
AsyncStorage.getItem('KeyUserEmail').then(value =>{
this.setState({ userEmail: value });
});
AsyncStorage.getItem('KeyUserProfilePicture').then(value =>{
this.setState({ userProfilePicture: value });
});
}
render() {
return (
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
onPress={this.toggleDrawer}
style={{padding: 15,}}
>
<Icon ios="ios-menu" android="md-menu" size={30} color="white" />
</TouchableOpacity>
</View>
);
}
}
const FirstStackNavigator = createStackNavigator({
First: {
screen: Dashboard,
navigationOptions: ({ navigation }) => ({
title: 'Dashboard',
headerLeft: <ScreensSetup navigationProps={navigation} />,
headerStyle: {
backgroundColor: 'rgb(216,21,88)',
},
headerTintColor: 'white',
}),
},
});
const SecondStackNavigator = createStackNavigator({
Second: {
screen: Workorders,
................
});
const ThirdStackNavigator = createStackNavigator({
Third: {
screen: Projects,
.............
});
const FourthStackNavigator = createStackNavigator({
Fourth: {
screen: Settings,
...............
});
DrawerContent = (props) => {
return (
<View>
<View style={{ backgroundColor: 'rgb(216,21,88)', height: 160,}}>
<Text>{this.state.userName}</Text> //No display of userName
</View>
<DrawerItems {...props} />
</View>
)
}
const DrawerNavigator = createDrawerNavigator(
{
Dashboard: {
//Title
screen: FirstStackNavigator,
navigationOptions: {
drawerLabel: 'Dashboard',
drawerIcon: () => (
<Icon ios="ios-heart" android="md-heart" size={30} color="black" />
),
},
},
Workorders: {
...
},
Projects: {
...
},
Settings: {
...
},
},
{
contentComponent: DrawerContent,
initialRouteName: 'Dashboard',
drawerWidth: 280,
drawerPosition: 'left',
gesturesEnabled: false,
headerMode: 'float',
contentOptions: {
labelStyle: {
color: 'black'
}
}
},
);
const styles = StyleSheet.create({...})
export default DrawerNavigator
You can try the following:
Promise.all([
AsyncStorage.getItem('KeyUserName'),
AsyncStorage.getItem('KeyUserEmail'),
AsyncStorage.getItem('KeyUserProfilePicture')
]).then(
([userName,userEmail,userProfilePicture])=>
this.setState({ userName,userEmail,userProfilePicture })
)

How to Refresh previous Drawer Screen calling from 2nd Screen in Drawer navigation in React-native?

In my React-Native project I am using one Navigation drawer. In that drawer I declared two screens - NoteMeHome and MakeNote. Initially the NoteMeHome starts as an initial route of Drawer Navigation. From the NoteMeHome.js (first screen) class I go to the MakeNote.js (Second Screen). In this MakeNote.js class I have used one form to fill. After filling the Form when user clikcs submit it will go back to the previos Screen ( NoteMeHome.js) and as there is an API call in this NoteMeHome.js class, it will refresh and show the submitted data.
But whenever I am going from MakeNote Screen to NoteMeHome Screen, It just changes to back stack but the the NoteMeHome Screen is not refreshed as it is showing the old data.
To control the Drawer flow and structure I have created a class named -
NavigationDrawerStructure.js
class NavigationDrawerStructure extends Component {
static navigationOptions = {
header: null ,
};
toggleDrawer = () => {
this.props.navigationProps.toggleDrawer();
};
render() {
return (
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity onPress={this.toggleDrawer.bind(this)}>
{/*Donute Button Image */}
<Icon name='menu'/>
</TouchableOpacity>
</View>
);
}
}
const FirstActivity_StackNavigator = createStackNavigator({
First: {
screen: NoteMeHome,
navigationOptions: ({ navigation }) => ({
title: 'Home',
headerLeft: <NavigationDrawerStructure navigationProps={navigation} />,
headerStyle: {
backgroundColor: '#FF9800',
},
headerTintColor: '#fff',
}),
},
});
const Screen2_StackNavigator = createStackNavigator({
Second: {
screen: MakeNote,
navigationOptions: ({ navigation }) => ({
title: 'Create Note',
headerLeft: <NavigationDrawerStructure navigationProps={navigation} />,
headerStyle: {
backgroundColor: '#FF9800',
},
headerTintColor: '#fff',
}),
},
});
const Drawer = createDrawerNavigator({
Screen1: {
screen: FirstActivity_StackNavigator,
navigationOptions: {
drawerLabel: 'Home',
drawerIcon: (
<Icon name='home' size={24}
/>
)
},
},
Screen2: {
screen: Screen2_StackNavigator,
navigationOptions: {
drawerLabel: 'Create Note',
drawerIcon: (
<Icon name='home' size={24}
/>
)
},
},
});
const DrawerNavigatorExample = createStackNavigator({
Drawer: { screen: Drawer, navigationOptions: { header: null } },
ScreenExternal: {
screen: ScreenExternal,
navigationOptions: { title: 'Screen External' },
},
});
export default createAppContainer(DrawerNavigatorExample);
In the MakeNote.js(Second Screen) I use one function submit to a POST request. Here's the code for that-
submit() {
this.setState({isLoading:true})
let collection = {}
collection.timestamp = this.state.timestamp,
collection.status = this.state.status,
collection.title = this.state.title,
collection.detail = this.state.detail,
collection.url = this.state.url,
collection.mail = this.state.mail,
collection.phone = this.state.phone,
collection.category = this.state.category
console.log('#HELLO:', collection);
var url = 'my url';
if(collection.title != '' ) {
if(this.state.isNetConnected != false) {
fetch(url, {
method: 'POST',
body: JSON.stringify(collection),
headers: new Headers({
'Content-Type' : 'application/json',
'token': 'abcd',
'jwt': this.state.getValue
})
}).then(response =>
{
this.setState({isLoading:false});
if (response.status !== 200) {
console.log('Status Code: ' + response.status);
return;
}
response.json().then(data =>{
console.log(data);
if(data.status == "saved") {
this.props.navigation.navigate('First');
}
});
}
)
.catch(error=>{
this.setState({isLoading:false})
console.error('Error:', error)
})
} else{
this.setState({isLoading:false});
Alert.alert("Oops!! No Internet Connection Available");
}
}
else {
this.setState({isLoading:false})
Alert.alert('Please fill up the required field');
}
}
In the submit function you can see the below line initialize the previous screen back-
if(data.status == "saved") {
this.props.navigation.navigate('First');
}
It shows the NoteMeHome(First Screen) back but no data has been refreshed.
So, I need a solution to refresh this NoteMeHome Screen and show the latest data by API call.
You can pass a callback function as parameter when you call navigate .
Inplace of :
this.props.navigation.navigate('First');
Add This:
this.props.navigation.navigate('First', {
onGoBack: () => this.refresh(),
});
Then add callback function :
refresh() {
this.doSomething();
}
Hope it helps !

Missing TabBar Review Icon when in Settings screen

When I am on settings screen, I see that the Review Icon (favorite) is missing. It shows when I am back on Review screen. Why is that happening. See the screenshot I took. Pasting relevant code snippet from my project for reference.
const MainNavigator = TabNavigator({
map: { screen: MapScreen },
deck: { screen: DeckScreen },
review: {
screen: StackNavigator({
review: { screen: ReviewScreen },
settings: { screen: SettingsScreen }
})
}
}, {
tabBarPosition: 'bottom',
tabBarOptions: {
labelStyle: { fontSize: 12 }
}
});
class ReviewScreen extends Component {
static navigationOptions = props => {
const {navigation} = props;
const {navigate} = navigation;
return {
tabBarIcon: ({ tintColor }) => {
return <Icon name="favorite" size={30} color={tintColor} />
},
headerTitle: 'Review Jobs',
headerRight: (
<Button
title="Settings"
onPress={() => navigate('settings')}
backgroundColor="rgba(0,0,0,0)"
color="rgba(0, 122, 255, 1)"
/>
)
}
}
Leads here is appreciated.
There is an issue with your code, since you're setting every icon's tintColor in the static navigationOptions as
tabBarIcon: ({tintColor}) => {
return <Icon name="favorite" size={30} color={tintColor}/>
}
and there is none for the Settings Screen, which also expects an Icon as it is inside the TabNavigator, therefore null is being rendered there.
Therefore you need to set the navigationOptions in the Settings Screen as
static navigationOptions = props => {
const {navigation} = props;
const {navigate} = navigation;
return {
tabBarIcon: ({tintColor}) => {
return <Icon name="favorite" size={30} color={tintColor}/>
}
}
}
or you may add the default icons in your App.js navigation file as
screen: TabNavigator({
map: { screen: MapScreen,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="favorite" size={30} color={tintColor}/>
)
}
},
// ....so on
Hope it helps!

Cannot read property 'routeName' of undefined react-navigation

I am trying use a custom component for my drawer layout. Below is my custom Drawer component:
CustomDrawer.js:
class CustomDrawer extends Component {
navigateToScreen = (route) => () => {
const navigateAction = NavigationActions.navigate({
routeName: route
});
this.props.navigation.dispatch(navigateAction);
}
render () {
return (
<View style={styles.container}>
<ScrollView>
<View>
<Text style={styles.sectionHeadingStyle}>
Section 1
</Text>
<View style={styles.navSectionStyle}>
<Text style={styles.navItemStyle} onPress={this.navigateToScreen('Page1')}>
Page1
</Text>
</View>
</View>
<View>
<Text style={styles.sectionHeadingStyle}>
Section 2
</Text>
<View style={styles.navSectionStyle}>
<Text style={styles.navItemStyle} onPress={this.navigateToScreen('Page2')}>
Page2
</Text>
<Text style={styles.navItemStyle} onPress={this.navigateToScreen('Page3')}>
Page3
</Text>
</View>
</View>
</ScrollView>
<View style={styles.footerContainer}>
<Text>This is my fixed footer</Text>
</View>
</View>
);
}
}
CustomDrawer.propTypes = {
navigation: PropTypes.object
};
export default CustomDrawer;
const styles = StyleSheet.create({
container: {
paddingTop: 20,
flex: 1
},
navItemStyle: {
padding: 10
},
navSectionStyle: {
backgroundColor: 'lightgrey'
},
sectionHeadingStyle: {
paddingVertical: 10,
paddingHorizontal: 5
},
footerContainer: {
padding: 20,
backgroundColor: 'lightgrey'
}
});
Below is my router.js:
const mapNavigationStateParamsToProps = (SomeComponent) => {
return class extends Component {
static navigationOptions = SomeComponent.navigationOptions; // better use hoist-non-react-statics
render() {
const {navigation: {state: {params}}} = this.props
return <SomeComponent {...params} {...this.props} />
}
}
}
export const MainScreenNavigator = TabNavigator({
Home: {
screen: Home,
navigationOptions : {
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor }) => <Icon name="account-circle" size={35} color={tintColor} />
},
},
MyCards: {
screen: MyCards,
navigationOptions : {
tabBarLabel: 'My Cards',
tabBarIcon: ({ tintColor }) => <Icon name="list" size={35} color={tintColor} />
},
},
},
{
tabBarPosition: 'bottom',
animationEnabled: true,
tabBarOptions: {
activeTintColor: '#e91e63',
},
},
);
export const drawerNavigation = DrawerNavigator({
Home: {
screen: Home,
},
MyCards: {
screen: MyCards,
},
Profile: {
screen: Profile,
},
SearchUsers: {
screen: SearchUsers
},
CardRequests: {
screen: CardRequests
},
GetCard: {
screen: GetCard
}
}, {
contentComponent: CustomDrawer,
drawerWidth: 300
}
);
drawerNavigation.navigationOptions = {
header: null,
};
export const AppNavigation = StackNavigator({
LoginScreen: { screen: Login},
SignUpScreen: { screen: SignUp },
Tabs: { screen: drawerNavigation},
AddCard: { screen: AddCard },
GetCard: {screen: GetCard},
SearchedUserProfile: {screen: mapNavigationStateParamsToProps(SearchedUserProfile) }
},
{
headerMode: 'screen'
});
When I run the app I am getting the following error:
Cannot read property 'routeName' of undefined
I am using routerName in CustomDrawer. Can anyone please tell me what I am doing wrong?
I fixed the issue by adding:
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
The complete Drawer Navigator:
export const drawerNavigation = DrawerNavigator({
Home: {
screen: Home,
},
MyCards: {
screen: MyCards,
},
Profile: {
screen: Profile,
},
SearchUsers: {
screen: SearchUsers
},
CardRequests: {
screen: CardRequests
},
GetCard: {
screen: GetCard
}
},{
contentComponent: SideMenu,
drawerWidth: 300,
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
});
Hope it helps

Categories

Resources