React Native - Conditional Back Button Based on State - react-native

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.

Related

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

Add button to header of React Navigation header

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",

How to send value with react-navigation in static filed?

I have a screen set a <Switch /> in header by using react navigation.
I know how to get this.state value from outside function by this.props.navigation.setParams.
But I don't know how to send the <Switch /> value to outside function.
I try it with the code onValueChange={value => params.handleThis(value)}
but in my case handleThis is key-value, it can't get the value obviously.
this.props.navigation.setParams({
handleThis: this.changeSwitch
});
How to send <Switch /> onChange value to outside function ?
Here is my code:
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
title: 'Title',
headerStyle: {
backgroundColor: ColorSetting.headerColor,
elevation: null,
},
headerTitleStyle: {
fontWeight: '300',
fontFamily: 'FFF Tusj',
fontSize: 18
},
headerRight:
<View style={{ marginRight: 15 }}>
<Switch
onValueChange={value => params.handleThis()}
value={params.switchOn}
onTintColor='#444444'
tintColor='#444444'
/>
</View>,
};
};
constructor(props) {
super(props);
this.state = {
switchOn: false
};
}
componentDidMount() {
this.props.navigation.setParams({
handleThis: this.changeSwitch
});
}
changeSwitch = () => {
const { switchOn, titleVersion } = this.state;
this.props.navigation.setParams({ switchOn: !switchOn });
this.setState({ switchOn: !switchOn });
}
Any help would be appreciated. Thanks in advance.
You can use params.handleThis as the handler, there is no need for an inline function.
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
title: 'SO Question',
headerStyle: {
elevation: null,
},
headerTitleStyle: {
fontFamily: 'FFF Tusj',
fontSize: 18
},
headerRight:
<View style={{ marginRight: 15 }}>
<Switch
onValueChange={params.handleThis}
value={params.switchOn}
onTintColor='#444444'
tintColor='#444444'
/>
</View>,
};
};
after that changeSwitch will receive the new value as the first parameter.
changeSwitch = (switchOn) => {
this.props.navigation.setParams({ switchOn });
this.setState({ switchOn });
}
Here's a working example

React-Native how to not run function when starting the app?

I am currently learning how to create app using React Native and I am running into the issue of why is my app running the method when the app just started running?
I thought that I am only calling the function componentDidMount() in my button onPress ?
everything works fine as intended but I am just not sure why that's happening.
Thanks for your help!
import React from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
export default class App extends React.Component {
constructor(props){
super(props)
this.state = {
isLoading: true,
text: ''
}
}
componentDidMount(summonerIGN){
console.log("This is in summonerIGN", summonerIGN)
return fetch('https://na1.api.riotgames.com/lol/summoner/v3/summoners/by-name/' + summonerIGN +'?api_key=<APIKey>')
.then((response) => response.json())
.then((responseJson) => {
console.log("This is in responseJson", responseJson)
console.log("This is the summoner ID: ", responseJson.id)
this.setState({
isLoading: false,
dataSource: responseJson,
summonerID: responseJson.id,
summonerName: responseJson.name,
})
})
.catch((error) => {
console.error(error)
})
}
render() {
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Search For Summoner!"
onChangeText={(text) => this.setState({
text: text
})}
/>
<Button
onPress={() => {
console.log("This is in this.state.text", this.state.text)
this.componentDidMount(this.state.text)
}}
title="Search"
color="#841584"
/>
<Text style={{padding: 10, fontSize: 20}}>
Searching for summoner: {this.state.text}
</Text>
<Text>
The summpner ID: {this.state.summonerID}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
You can't just call componentDidMount().
It gets executed automatically once your component has been loaded.
Instead of writing logic in componentDidMount(), write a separate function and call that function.
componentDidMount() is a lifecycle method.
Lifecycle method gets called automatically based on components loads.
export default class App extends React.Component {
constructor(props){
super(props)
this.state = {
isLoading: true,
text: ''
}
}
callApi = (summonerIGN) => {
console.log("This is in summonerIGN", summonerIGN)
return fetch('https://na1.api.riotgames.com/lol/summoner/v3/summoners/by-name/' + summonerIGN +'?api_key=<APIKey>')
.then((response) => response.json())
.then((responseJson) => {
console.log("This is in responseJson", responseJson)
console.log("This is the summoner ID: ", responseJson.id)
this.setState({
isLoading: false,
dataSource: responseJson,
summonerID: responseJson.id,
summonerName: responseJson.name,
})
})
.catch((error) => {
console.error(error)
})
}
render() {
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Search For Summoner!"
onChangeText={(text) => this.setState({
text: text
})}
/>
<Button
onPress={() => {
console.log("This is in this.state.text", this.state.text)
this.callApi(this.state.text)
}}
title="Search"
color="#841584"
/>
<Text style={{padding: 10, fontSize: 20}}>
Searching for summoner: {this.state.text}
</Text>
<Text>
The summpner ID: {this.state.summonerID}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

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?