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 !
Related
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
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",
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>
);
}
I am trying get re render a screen every time the user presses a tab or moving from one tab to another and then to first tab. I also placed a custom header in the screen. Same header on all the other tabs too getting some state from Asynchronous storage but it is also not refreshing once the screen has loading. I am using react navigation. Is there any method in navigation which will be called whenever tab is focused.
const AppStack = createBottomTabNavigator(
{
Search: SearchScreen,
Saved: SavedScreen,
Explore: ExploreStack,
Offers: OffersScreen,
Profile: ProfileScreen,
},
{
initialRouteName: 'Explore',
navigationOptions: ({navigation})=>({
tabBarIcon: ({focused, tintColor})=>{
const { routeName } = navigation.state;
let iconName, iconSize;
switch(routeName) {
case "Search":
iconName = `ios-search${focused ? '' : '-outline'}`;
break;
case "Saved":
iconName = `ios-heart${focused ? '' : '-outline'}`;
break;
case "Explore":
iconName = `ios-navigate${focused ? '' : '-outline'}`;
break;
case "Offers":
iconName = `ios-pricetag${focused ? '' : '-outline'}`;
break;
case "Profile":
iconName = `ios-person${focused ? '' : '-outline'}`;
break;
default:
break;
}
return <Icon name={iconName} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'black',
inactiveTintColor: 'grey',
activeBackgroundColor: '#abaf9b',
labelStyle: {
fontSize: 15,
},
// style for tabbar
style: {
backgroundColor: '#ffffff',
height: 60,
justifyContent: 'space-around',
alignContent: 'center',
alignItems: 'center',
},
// style for tab
tabStyle: {
paddingTop: 7,
paddingBottom: 7
}
},
}
)
This is one of the tab. Other tabs a very similar using same component but the different apis.
import React, { Component } from 'react';
import { View, Image, ActivityIndicator, TouchableWithoutFeedback, TouchableHighlight, AsyncStorage } from 'react-native';
import HeaderComponent from '../components/Header';
import SomeComponent from '../components/Some';
import { Container, Content, Icon, Spinner} from 'native-base';
class FirstScreen extends Component{
constructor(props){
super(props)
this.state = {
somelist: [],
name: '',
userId: '',
isloading: true,
location: ''
};
this.getUser();
}
componentDidMount(){
this.getLocation();
}
getLocation = async() => {
const result = await AsyncStorage.getItem('location');
console.log("Location " +result)
this.setState({location: result});
}
getUser = async() => {
const result = await AsyncStorage.getItem('user');
const data = JSON.parse(result);
console.log("data : "+data)
this.setState({name: data.name, userId: data.userId})
}
componentWillMount(){
console.log("Component will mount")
//For demo
fetch('http://someapi.co/api/listing')
.then(response => response.json())
.then(data => {
this.setState({ somelist: data, isloading: false }, function(){console.log(this.state.somelist, this.state.isloading)})
})
.catch(function(error){
console.log("Error : "+error);
});
//console.log(this.state.barlist);
}
renderComponent(){
if(this.state.isloading == true){
return (
<View style={{ flex: 1, justifyContent: 'center', height: 300 }}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
}
return this.state.somelist.map( user=>
<SomeComponent key={user.id}
onPress={()=>this.props.navigation.navigate('OtherScreen', {'Id': user.id, 'userId': this.state.userId})}
image={user.image[0].imageName}
/>
);
}
render(){
console.log(this.state.userId)
return (
<Container>
<HeaderComponent
profilePress={()=>this.props.navigation.navigate('Profile')}
seachPress={()=>this.props.navigation.navigate('SearchSetting')}
// location={this.state.location}
/>
<TouchableHighlight
style={{position: 'absolute', bottom: 20, zIndex:999999, right: 20 }}
onPress={()=>this.props.navigation.navigate('Map')}
>
<Image source={require('../images/navigation_icon.png')} style={{height: 50, width: 50}}/>
</TouchableHighlight>
<Content>
<View style={{padding: 0, margin: 0}}>
{this.renderComponent()}
</View>
</Content>
</Container>
);
}
}
export { SomeScreen };
You can access the event listeners in react-navigation as mentioned here
// call when the screen is focused
componentDidMount () {
this._onFocusListener = this.props.navigation.addListener('didFocus', (payload) => {
// refresh the component here
// or update based on your requirements
});
}
Please refer the link: https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle
There are 2 different ways I found below,
// call when the screen is focused
componentDidMount () {
this._navListener = this.props.navigation.addListener('didFocus', (payload) => {
// update based on your requirements
});
}
OR
import { NavigationEvents } from "react-navigation";
...
class HomeScreen extends React.Component {
render() {
return (
<View>
<NavigationEvents
onWillFocus={() => {
// update based on your requirements!
}}
/>
<Text>Home</Text>
</View>
);
}
}
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?