Add button to header of React Navigation header - react-native

I have this compononet of mine ... where i want to draw a logout button and also hide the back arrow . but i am not able to do so . Can anyone tell me where i am doing it wrong? I have followed the original documentation of react navigation as well but no solution.
class Welcome extends React.Component {
constructor(props) {
super(props)
this.state = {
user: this.props.navigation.state.params.user,
}
}
static navigationOptions = ({ navigation}) => {
const { params = {} } = navigation ;
return {
headerTitle : "Welcome",
headerLeft: null,
headerRight : (
<TouchableOpacity
style={{ backgroundColor: '#29434e' , padding: 10}}
onPress={() => params.onlogout}
>
<Text style={{ marginVertical:5, color: 'rgba(255,255,255,0.7)', fontSize: 20,}}> Logout </Text>
</TouchableOpacity>
)
};
};
_Logout = () => {
this.props.Signout();
};
componentDidMount() {
this.props.navigation.setParams({ onlogout : this._Logout , isSaving: false})
}
}
}
const mapDispatchToProps = (dispatch) => {
return {
Signout: () => dispatch(Signout())
}
}
export default connect(null,mapDispatchToProps)(Welcome)

That is because of the marginVertical:10 on your Text style. Remove it and you should see your button.
Here is a working example: https://snack.expo.io/rJOSqqEHS

So the problem is component is not getting the header options using static navigationOptions thing but when i try the defaultNavigationOptions it is working perfect and the code is as belows :
const otherApp = createStackNavigator({
Welcome : {
screen : WelcomeScreen
}
},
{
defaultNavigationOptions : ({navigation}) => ({
title : 'Welcome',
headerStyle: {
backgroundColor: '#29434e',
shadowColor: 'transparent',
elevation: 0
},
headerRight: (
<TouchableOpacity
style={{ backgroundColor: '#DDDDDD', padding: 5 }}
onPress={() => navigation.getParam('logout')}>
<Text
style={{
fontSize: 10,
}}>
Logout
</Text>
</TouchableOpacity>
),
})
});
Hope it will help someone in future.
My environment is :
"react": "16.8.6",
"react-native": "0.60.3",
"react-navigation": "^3.11.0",

Related

react-native: drawer is not opening

I am new to react-native and i am trying to add drawer to my app.
I am sharing my code with you.
export default class Dashboard extends React.Component {
constructor(props){
super(props);
this.state = {
}
}
closeControlPanel = () => {
this._drawer.close()
};
openControlPanel = () => {
this._drawer.open()
};
const drawerStyles = {
drawer: { shadowColor: '#000000', shadowOpacity: 0.8,backgroundColor:'' shadowRadius: 3},
main: {paddingLeft: 3},
}
render(){
let drawerContent = [];
drawerContent.push(
<View style={{ backgroundColor:'red', textAlign:'center' }}>
<Text style={{ color:'#ffffff', fontSize:17, alignSelf:'center' }}>Hiiiiiiiiiii</Text>
</View>
);
<Drawer
type="overlay"
tapToClose={true}
content = {drawerContent}
openDrawerOffset={0.2} // 20% gap on the right side of drawer
panCloseMask={0.2}
closedDrawerOffset={-3}
styles={drawerStyles}
tweenHandler={(ratio) => ({
main: { opacity:(2-ratio)/2 }
})}
>
</Drawer>
}
}
can anyone tell me where should be content of drawer and how to write open and close drawer functionality.Thank you...
you have not given the ref property on Drawer
Try adding this .
<Drawer
ref={c => this._drawer = c} // <==== Add this line
type="overlay"
tapToClose={true}
content = {drawerContent}
openDrawerOffset={0.2} // 20% gap on the right side of drawer
panCloseMask={0.2}
closedDrawerOffset={-3}
styles={drawerStyles}
tweenHandler={(ratio) => ({
main: { opacity:(2-ratio)/2 }
})}
>
</Drawer>

Method is not being invoked when i press the button

I am using react navigation and have added a button on the right to signout from my app using default navigation options as shown below :
const otherApp = createStackNavigator({
Welcome : {
screen : WelcomeScreen
}
},
{
defaultNavigationOptions : ({navigation}) => ({
title : 'Welcome',
headerStyle: {
backgroundColor: '#29434e',
shadowColor: 'transparent',
elevation: 0
},
headerRight: (
<TouchableOpacity
style={{ backgroundColor: '#DDDDDD', padding: 5 }}
onPress={() => navigation.getParam('logout')}>
<Text
style={{
fontSize: 10,
}}>
Logout
</Text>
</TouchableOpacity>
),
})
});
And i am binding the method to be invoked as follows :
_Logout() {
this.props.signOut();
}
componentDidMount(){
this.props.navigation.setParams({ logout : this._Logout.bind(this) })
}
Function is maped to props using the redux
const mapDispatchToProps = (dispatch) => {
return {
Signout : ()=> dispatch(Signout())
}
}
But the problem is when i press the button, it does not invoke the method !
You can also use onFocus method of react-navigation to see whether the screen is on focus or not. If the screen is on focus then call the function.
import { withNavigation } from "react-navigation";
componentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener("didFocus", () => {
// The screen is focused
// Call any action
});
}
componentWillUnmount() {
// Remove the event listener
this.focusListener.remove();
}
export default withNavigation(Your Class Name);
Method : 2
import { NavigationEvents } from "react-navigation";
onFocus = () => {
//Write the code here which you want to do when the screen comes to focus.
};
render() {
return (
<NavigationEvents
onDidFocus={() => {
this.onFocus();
}}
/>
)}
I got the solution, what i was doing wrong that i wasn't passing a call back in the onPress of the button !
logoutFromFirebase = () => {
this.props.Signout();
this.props.navigation.navigate(this.props.user.uid ? 'App' : 'Auth')
}
componentDidMount(){
this.props.navigation.setParams({ logout : this.logoutFromFirebase })
}
And default navigation
defaultNavigationOptions : ({navigation}) => ({
title : 'Welcome',
headerStyle: {
backgroundColor: '#29434e',
shadowColor: 'transparent',
elevation: 0
},
headerRight: (
<TouchableOpacity
style={{ backgroundColor: '#DDDDDD', padding: 5 }}
onPress={navigation.getParam('logout' , () => Reactotron.log('Logout not callled'))}>
<Text
style={{
fontSize: 10,
}}>
Logout
</Text>
</TouchableOpacity>
),
})
And now it is working fine !

Update username in navigation drawer when login with diffrent user in react native

I am showing username in drawernavigator, but when I logged out and logged in again with a different user, the username is not updating, it's showing old username.
I am also using didFocus Listener but it is also not working please help
import React, { Component } from 'react';
import { View, Image,Text, TouchableOpacity,MenuImage,navigation,AsyncStorage, Alert } from 'react-native';
import {widthPercentageToDP as wp, heightPercentageToDP as hp} from 'react-native-responsive-screen';
import {
createDrawerNavigator,
createStackNavigator,
DrawerItems,
} from 'react-navigation';
import WalkThrow from '../pages/WalkThrow';
import Login from '../pages/Login';
import Register from '../pages/Register';
class NavigationDrawerStructure extends Component {
static propTypes = {
navigation: functionTypes.isRequired,
};
toggleDrawer = async() => {
this.props.navigationProps.openDrawer();
};
render() {
return (
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity onPress={this.toggleDrawer.bind(this)}>
<Image
source={require('../assets/images/menu48.png')}
style={{ width: 25, height: 25, marginLeft: 15 }}
/>
</TouchableOpacity>
</View>
);
}
}
class NavigationImage extends Component {
toggleDrawer = async() => {
this.props.navigationProps.openDrawer();
};
render() {
return (
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity onPress={this.toggleDrawer. bind(this)}>
<Image
source={require('../assets/images/user.png')}
style={{ width: 40, height: 40, marginRight:15 }}
/>
</TouchableOpacity>
</View>
);
}
}
class ShowUserName extends Component{
constructor(props) {
super(props);
this.state = {
getname:''
}
}
componentDidMount= async()=>{
let getname = await AsyncStorage.getItem('userName');
this.setState({getname:getname});
const { navigation } = this.props;
this.focusListener = navigation.addListener("didFocus", async() => {
let getname = await AsyncStorage.getItem('userName');
this.setState({getname:getname});
});
}
render() {
return (
<View>
<Text style={{color:'#fff',fontSize:23,padding:5}}>
{this.state.getname}
</Text>
</View>
);
}
}
const Logout= createStackNavigator({
Register:{
screen:Register,
navigationOptions:{
header: null,
},
},
Login: {
screen: Login,
navigationOptions:{
header: null,
}
},
ForgetPassword: {
screen: ForgetPassword,
navigationOptions:{
header: null,
}
},
initialRouteName: 'WalkThrow',
});
const Profile_StackNavigator = createStackNavigator({
Profile: {
initialRouteName: 'Profile',
screen:ChangeName,
navigationOptions: ({ navigation }) => ({
title: 'Profile',
headerLeft: <NavigationDrawerStructure navigationProps={navigation} />,
headerStyle: {
backgroundColor: '#3598db',
shadowOpacity: 0,
elevation: 0,
},
headerTintColor: '#fff',
}),
}
});
const ChangePassword_StackNavigator = createStackNavigator({
ChangePassword: {
initialRouteName: 'WalkThrow',
screen:ChangePassword,
navigationOptions: ({ navigation }) => ({
title: 'Change Password',
headerLeft: <NavigationDrawerStructure navigationProps={navigation} />,
headerStyle: {
backgroundColor: '#3598db',
shadowOpacity: 0,
elevation: 0,
},
headerTintColor: '#fff',
}),
},
});
const DashBoard_StackNavigator = createStackNavigator({
WalkThrow: {
screen: WalkThrow,
navigationOptions:{
header: null,
},
},
const DrawerContent = (props) => (
<View>
<View
style={{
backgroundColor: '#3598db',
height: 200,
alignItems: 'center',
justifyContent: 'center',
}}>
<Image
source={require('../assets/images/user.png')}
style={{ width:'36%', height: '50%' }}
/>
<ShowUserName/>
</View>
<DrawerItems {...props} />
</View>
)
export default createDrawerNavigator({
ChangePassword: {
screen: ChangePassword_StackNavigator,
initialRouteName: 'Logout',
navigationOptions: {
drawerLabel: 'Change Password',
drawerIcon: () => (
<Image
source={require('../assets/images/user48.png')}
style={{width: 25, height: 25, }}
/>
)
},
},
Logout: {
screen: Logout,
initialRouteName: 'Logout',
navigationOptions: {
drawerLabel: 'Logout',
drawerIcon: () => (
<Image
source={require('../assets/images/user48.png')}
style={{width: 25, height: 25,}}
/>
)
},
},
},
{
contentComponent: DrawerContent,
});
I am showing username in drawernavigator,but when i logged out and login again with different user the username is not updating,its showing old username.when different user login then show the username for those user
the issue is that this doesn't rerun because the drawer does not unmount
componentDidMount= async()=>{
let getname = await AsyncStorage.getItem('userName');
this.setState({getname:getname});
const { navigation } = this.props;
this.focusListener = navigation.addListener("didFocus", async() => {
let getname = await AsyncStorage.getItem('userName');
this.setState({getname:getname});
});
}
the bigger problem with the architecture of your app is that you' using asyncStorage as your state management it isa huge anti pattern and will make your app really slow and battery draining in the long run.
you have to use some sort of state management ie Context API or redux, then you get your userName straight form the global state it will make the app much faster and solve your problem and probably many others you are experiencing
then your render will look something like this without any need to to set the state again in a lifeCycle Method, you may also need a default depending where you declared the drawer
render() {
return (
<View>
<Text style={{color:'#fff',fontSize:23,padding:5}}>
{this.props.SOMEGLOBALCONTEXTHERE.username || ''}
</Text>
</View>
);
}

React Native - Conditional Back Button Based on State

Below is a simple example of what I'm trying to do. I have headerLeft in the navigation bar, but would like to make that disappear (so the user can't click back) when the user clicks the button (Take the Challenge) and thereby sets the state variable 'next' to true. Is this possible to do? If so, any suggestions on how?
export default class Challenge extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: "5 Day Challenge",
headerTintColor: "white",
headerStyle: { backgroundColor: "black" },
headerTitleStyle: { fontWeight: "bold", fontSize: moderateScale(15) },
headerLeft: (
<Button onPress={() => navigation.goBack(null)}>
{" "}
<MaterialCommunityIcons
name="arrow-left"
size={28}
/>
</Button>
)
});
constructor(props) {
super(props);
this.state = {
next: false,
};
}
render() {
return (
<View>
<Button
onPress={() =>
this.setState({ next: true })}
>
<Text >
TAKE THE CHALLENGE
</Text>
</Button>
</View>
)}
}
You can use the this.props.navigation.setParams({...});
export default class Challenge extends React.Component {
static navigationOptions = ({ navigation }) => {
const { state } = navigation;
if (state.params !== undefined) {
return {
title: "5 Day Challenge",
headerTintColor: "white",
headerStyle: { backgroundColor: "black" },
headerTitleStyle: { fontWeight: "bold", fontSize: moderateScale(15) },
headerLeft: state.params.showBack ? (
<Button onPress={() => navigation.goBack(null)}>
{" "}
<MaterialCommunityIcons
name="arrow-left"
size={28}
/>
</Button>
) : null
}
}
};
constructor(props) {
super(props);
this.state = {
next: false,
};
}
componentDidMount() {
this.props.navigation.setParams({
showBack: true
});
}
onClick = () => {
this.setState({ next: true })
this.props.navigation.setParams({
showBack: false
});
}
render() {
return (
<View>
<Button
onPress={() => this.onClick()} >
<Text >
TAKE THE CHALLENGE
</Text>
</Button>
</View>
)
}
}
PS : I hope it will work, I didn't test that code.

Setting styles to a custom component inside navigationOption in react native

I want to set styling for a custom component, inside navigationOptions in react native. But the stying doesnt work and it give an error saying. Same styling is working in another text box of the same screen.
P:S: I could achieve this by doing this? Am I doing this correct? Is this the proper way of handling this?
class WorkoutScreen extends Component {
constructor(props) {
super(props);
this.state = {
searchText: ""
};
}
componentDidMount() {
this.props.navigation.setParams({
searchWorkouts: this.searchWorkoutHandler,
onChangeSearchText: this.onChangeSearchTextHandler,
searchText: this.state.searchText
});
}
// on change search text
onChangeSearchTextHandler = value => {
this.setState({
searchText: value
});
this.props.navigation.setParams({
searchText: value
});
};
// search workouts
searchWorkoutHandler = () => {
alert("Searching Workouts");
};
render() {
return (
<View style={styles.container}>
<Text>Im Here</Text>
</View>
);
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerTitle: (
<SearchInput
style={styles.searchInput}
value={params.searchText}
source={Search}
borderRadius={50}
placeholder="Search / Filter"
onChangeText={value => params.onChangeSearchText(value)}
onPress={() => params.searchWorkouts()}
/>
),
headerTitleStyle: { width: "100%", alignItems: "center" },
headerStyle: {
paddingRight: 10,
paddingLeft: 10
},
headerLeft: (
<ClickableIcon
source={Bookmark}
onIconPressed={() => alert("Notifications Clicked Workout")}
/>
),
headerRight: (
<ClickableIcon
source={Movements}
onIconPressed={() => alert("Add New Clicked")}
/>
)
};
};
}
const styles = StyleSheet.create({
container: {
flex: 1
},
scrollView: {
backgroundColor: "#ffffff"
},
searchInput: {
height: 45,
color: "gray",
fontSize: 18
}
});
export default WorkoutScreen;
How can I overcome this?