React Navigation - How to use this.state in Header navigationOptions? - react-native

I have spent a couple hours to find a code to handle the state in navigationOptions, but I don't get it till now,
I have a code :
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state
return {
headerLeft: <FontAwesome name='arrow-left' size={20} color="#FFF" onPress={() => navigation.navigate('Home')} style={{margin: DeviceWidth*0.04}}/>,
// here I want to show the TextInput if the `HeaderRight pressed` and show the `String` for the first time
headerTitle: this.state.showSearch ? <TextInput
placeholder="this is placeholder"
placeholder="search"
underlineColorAndroid='transparent'
placeholderTextColor= 'gray'
minWidth={DeviceWidth*0.75}
style={{borderWidth:1, borderColor:'grey', backgroundColor:'white', borderRadius:50}}
/> : 'My Patient',
// Here I want to set the state of `showSearch` to visible at `onPress`
headerRight: <FontAwesome name='search' size={20} color="#FFF" onPress={() => params.handleRemove()} style={{margin: DeviceWidth*0.04}}/>,
}
}
componentDidMount () {
this.props.navigation.setParams({ handleRemove: this.removeVehicle })
}
removeVehicle = () => {
this.setState({showSearch: !this.state.showSearch})
}
constructor(props){
super(props);
this.state = {showSearch: false}
}
when I run the code, I have an error
TypeError: undefined is not an object (evaluating '_this3.state.showSearch')
It is possible to show/hide the headerTitle depending on this.state.showSearch?

You can do this in the following easy way
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state
return {
headerTitle: params.showSearch ? 'New Title' : 'Alternate Title'
// Similarly for the rest
}
}
changeTitle = () => {
const {showSearch} = this.state
// Assuming you have access to the navigation props
this.props.navigation.setParams({
showSearch
})
this.setState({showSearch: !showSearch})
}

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: ''
};
}
}

navigationOptions in screen component is not working

If I set the navigationOptions in the screen component inside a StackNavigator to change the header of the stack inside a DrawerNavigator the navigationOptions are not taken into account. I can however pass defaultNavigationOptions which are taken into account. If I don't pass defaultNavigationOptions it doesn't change the behavior. This is with react-navigation v 4.x
here I create the Stack navigators screens for my Drawer
drawerNavigator = () => {
let drawerRoutes = {}
this.state.routes.forEach(r => {
let stackRoute = {}
let t = () => <First name={r} />
stackRoute[r] = { screen : t }
let stackOptions = {
initialRouteName: r,
defaultNavigationOptions: stackNavigationOptions
}
let s = createStackNavigator(stackRoute, stackOptions)
drawerRoutes[r] = { screen : s }
})
let drawerOptions = {
drawerWidth: '75%',
initialRouteName: this.state.routes[0],
contentComponent: props => <Menu {...props} />,
contentOptions: drawerItemsOptions
}
return createDrawerNavigator(drawerRoutes, drawerOptions);
}
Then in my component I try to set the navigationOptions of the Stack but it is not taken into account...
export default class First extends Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: 'hahahaha',
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#fff"
/>
),
}
}
render() {
return (
<SafeAreaView style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<StatusBar transluscent backgroundColor="transparent" barStyle="dark-content" />
<Text>{this.props.name}</Text>
</SafeAreaView>
);
}
}
Okay I've discovered that if I pass my screen that way
stackRoute[r] = { screen : First }
instead of the
let t = () => <First name={r} />
stackRoute[r] = { screen : t }
the navigationOptions are taken into account. However I cannot pass props that way....

how i can check condition inside navigationOptions in react native

I want to set a custom navigation headerLeft property;How i can set the headerLeft property only if the condition is satisfied.otherwise not.I use a StackNavigator in my app. and also cannot access props from inside navigationOptions.
here is my code
componentDidMount() {
this.props.navigation.setParams({goBack: this.goBack, shouldShow: this.state.show });
}
onShow = () => {
if (this.state.steps >1) {
this.setState({show:true}, () => {this.props.navigation.setParam({shouldShow:true})} )
}
}
goBack= () =>{
this.setState({ steps: this.state.steps - 1 })
}
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
let headerLeft = null;
if (params.shouldShow) {
headerLeft = <TouchableOpacity onPress={() => params.goBack}>
<Image source={/* src */} style={Styles.navBarIcon} />
</TouchableOpacity>
}
return {
headerLeft: headerLeft,
headerTitle: 'Register',
headerStyle: Styles.navBar,
};
};
Since navigation options are static and not included inside your class, you cannot simply access states inside it. But you can follow below method.
// ------ inside your class -------
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
let headerLeft = null;
if(params.shouldShow) {
headerLeft = <TouchableOpacity onPress={() => params.onPressButton()}>
<Image source={/* source */} style={Styles.navBarIcon} />
</TouchableOpacity>
}
return {
headerLeft: headerLeft
};
};
componentDidMount(){
this.props.navigation.setParams({ onPressButton: this.onButtonPressedEvent, shouldShow:this.state.show });
}
onButtonPressedEvent = () =>{
// What you want to do when button pressed.
}
onShow = () =>{
if(this.state.count > 1){
this.setState({show:true}, () => {this.props.navigation.setParam({shouldShow:true})} )
}
}
Remember to call onShow method when you change the count to make changes in navigation.

Update value in StackNavigation custom header

I defined a custom view as a component for headerRight property in navigationOptions as bellow:
static navigationOptions = ({ navigation }) => {
return {
headerRight: navigation.getParam('headerRight', null),
};
};
and then in componentDidMount:
this.props.navigation.setParams({
headerRight: (<MessageDetailsHeader {...this.props}
title = {this.state.headerTitle}
subTitle = {this.state.headerSubTitle}
online = {this.state.online}
/>)
})
also i defined some state for updating values:
constructor(props) {
super(props);
this.state = {
headerTitle: null,
headerSubTitle: null,
headerOnline: false
};
}
Custom header view component defined as bellow:
export default class MessageDetailsHeader extends React.Component {
constructor(props) {
super(props);
this.state = {
title: this.props.title,
subTitle: this.props.subTitle,
online: this.props.online
};
}
componentWillReceiveProps(nextProps) {
this.setState({
title: nextProps.title,
subTitle: nextProps.subTitle,
online: nextProps.online,
})
}
render() {
return (
<View style={styles.headerContainer}>
<View style={styles.headerDetailsContainer}>
<Text style={styles.headerTitle}>{this.state.title}</Text>
<Text style={styles.headerSubTitle}>{this.state.subTitle}</Text>
</View>
<Avatar small rounded source={require('../images/no-profile.png')} activeOpacity={0.7} avatarStyle={this.state.online? styles.avatarOnline: styles.avatarOffline}/>
</View>
);
}
}
I need to call setState in screen and then update Custom View in navigation header, is this a right way?
Thanks in advance
Solved!
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
return {
headerRight: <MessageDetailsHeader
title={params.headerTitle}
subTitle={params.headerSubTitle}
online={params.headerOnline}
/>
};
};
and then called bellow code to set new value, easily!
this.props.navigation.setParams({
headerSubTitle: 'online',
});

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 });