Cant navigate to next screen - react-native

I am trying to navigates screens with a stack navigator. The Idea is, my app will navigate from my list of chats, too the chat screen. However when I try to navigate to the next screen, I receive an error saying "undefined is not an object" on this.props.navigation. Here is what my code looks like:
MainTabNavigator (Contains my stack navigator)
const ChatListStack = createStackNavigator({
ChatList:ChatListScreen,
ChatView:ChatScreen,
});
ChatListStack.navigationOptions = {
tabBarLabel: 'ChatList',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? `ios-options${focused ? '' : '-outline'}` :
'md-options'}
/>
),
};
ChatListScreen (Where the navigation starts from)
export default class ChatListScreen extends Component {
constructor(props) {
super(props);
}
static navigationOptions = {
title: "Chats"
};
renderRow({ item }) {
return (
<TouchableHighlight
onPress={() => this.props.navigation.navigate("ChatView")}
>
<ListItem
roundAvatar
title={item.name}
subtitle={item.subtitle}
avatar={{ uri: item.avatar_url }}
/>
</TouchableHighlight>
);
}
goToChat() {}
render() {
return (
<View style={styles.mainContainer}>
<SearchBar
lightTheme
icon={{ type: "font-awesome", name: "search" }}
placeholder="Type Here..."
/>
<List style={styles.listContainerStyle}>
<FlatList
data={users}
renderItem={this.renderRow}
keyExtractor={item => item.name}
/>
</List>
</View>
);
}
}
Chat(This is the target Chat screen)
export default class ChatScreen extends Component {
constructor() {
super();
this.state = {
messages: []
};
}
componentWillMount() {
this.setState({
messages: [
{
_id: 1,
text: "Hello test",
createdAt: new Date(),
user: {
_id: 2,
name: "dude"
}
}
]
});
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={message => this.onSend(message)}
user={{
_id: 1
}}
/>
);
}
}

to solve this problem you must make a function to handle navigation then bind it in the contractor then send it in your Touchableopacity

Related

How to use useLayoutEffect in Class component

The documentation here https://reactnavigation.org/docs/header-buttons says I need to use useLayoutEffect to trigger a function from another file but I am using a class component and have no plan to switch to that other type.
How do I re write this file so I can get access to navigation.getParam because now it's undefined
export default class Dashboard extends React.Component {
constructor() {
super();
this.state = {
};
}
onLogout = () => {
alert.alert('Alert', 'Testing...')
}
componentDidUpdate(){
this.props.navigation.setParams({ onLogout: this.onLogout});
}
componentDidMount() {
this.props.navigation.setParams({ onLogout: this.onLogout});
}
render() {
return (
<View style={styles.container} />
);
}
}
Navigation code
<BottomTab.Screen
name="Dashboard"
component={Dashboard}
options={({ route, navigation }) => ({
// title: "Dashboard",
tabBarIcon: () => <Icon name="printer" size={20} color="black" />,
headerLeft: () => (
<Icon style={{ marginLeft: 14 }} name="user" size={20} color="black" />
),
headerRight: (
<Button
onPress={navigation.getParam('onLogout')}
/>
)
})}
/>

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

React Native reusable edit component

I'm trying to create a reusable component in react native. The idea is to have only one component responsible to edit all the fields that I have.
Main Component
...
constructor(props) {
super(props);
this.state.FirstName = 'Joe'
}
...
const { FirstName } = this.state.FirstName;
<TouchableOpacity
onPress={() =>
NavigationService.navigate('EditData', {
label: 'First Name',
initialValue: FirstName,
onSubmit: (FirstName) => this.setState({ FirstName })
})
}
>
<CardItem>
<Left>
<FontAwesome5 name="user-edit" />
<Text>First Name</Text>
</Left>
<Right>
<Row>
<Text style={styles.valueText}>{FirstName} </Text>
<Icon name="arrow-forward" />
</Row>
</Right>
</CardItem>
</TouchableOpacity>
// Keep doing the same for other fields
Then, the edit component should be reusable.
constructor(props) {
super(props);
// callback function
this.onSubmit = props.navigation.getParam('onSubmit');
// label/value
this.state = {
label: props.navigation.getParam('label'),
value: props.navigation.getParam('initialValue')
};
}
render() {
const { onSubmit } = this;
const { label, value } = this.state;
return (
<Container>
<Header />
<Content>
<Item floatingLabel style={{ marginTop: 10 }}>
<Label>{label}</Label>
<Input
value={value}
onChangeText={val => this.setState({ value: val })}
/>
</Item>
<Button
onPress={() => {
onSubmit(value);
NavigationService.navigate('TenantDetails');
}
}
>
<Text>OK</Text>
</Button>
</Content>
</Container>
);
}
When back to the main component, the first name value was not changed.
My NavigationService in case it might be the problem:
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
};
Thanks
You could pass a callback to your new component which handles this. The new component would start with a state with the initialValue set. It looks like you might be using react-navigation so I would recommend that if you want this component on its own screen you could do
this.navigation.navigate('SetValueScreen', {
initialValue: this.state.email,
onSubmit: (email) => this.setState({ email })
})
and on the SetValueScreen get the initialValue in the constructor and in the render use the callback
class SetValueScreen extends React.PureComponent{
constructor(props){
super(props)
this.onSubmit = props.navigation.getParam('onSubmit');
this.state = {
value: props.navigation.getParam('initialValue')
}
}
render(){
const { onSubmit } = this
const { value } = this.state
return (
...
<Right>
<TextInput value={value} onChangeText={(value) => setState({ value })} />
</Right>
<Button onPress={() => {
onSubmit(value)
navigation.goBack()
}} >
OK
</Button>
...
)
}
}
I hope this helps.

React Native component life cycle - which function gets called when screen is visible

I've been looking at the documentation here https://reactjs.org/docs/react-component.html, but there is something that keeps bugging me. A pattern which I almost constantly seem to need but I am unable to find a solution to it and so always have to find hacks around it.
The pattern that I'm talking about is as follows. My app has a TabNavigator and I understand that when the app gets initialised ComponentDidMount is called on all the tabs. What I would like are functions that get called when a Tab is either navigated to, using this.props.navigation.navigate('TAB1') or when the tab is clicked at the bottom of the screen.
If someone can help with this I'd really appreciate it. Apologies there is no code to show for this.
Thanks
First, to understand the reason why it's not so easy, read the following conversation in the corresponding issue: https://github.com/react-navigation/react-navigation/issues/51
Here is what currently looks like the most effective solution: https://github.com/react-navigation/react-navigation/pull/3345
And this is the example code you can try out:
import type {
NavigationScreenProp,
NavigationEventSubscription,
} from 'react-navigation';
import React from 'react';
import { Button, Platform, ScrollView, StatusBar, View } from 'react-native';
import { SafeAreaView, TabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import SampleText from './SampleText';
const MyNavScreen = ({ navigation, banner }) => (
<SafeAreaView forceInset={{ horizontal: 'always', top: 'always' }}>
<SampleText>{banner}</SampleText>
<Button
onPress={() => navigation.navigate('Home')}
title="Go to home tab"
/>
<Button
onPress={() => navigation.navigate('Settings')}
title="Go to settings tab"
/>
<Button onPress={() => navigation.goBack(null)} title="Go back" />
<StatusBar barStyle="default" />
</SafeAreaView>
);
const MyHomeScreen = ({ navigation }) => (
<MyNavScreen banner="Home Tab" navigation={navigation} />
);
MyHomeScreen.navigationOptions = {
tabBarTestIDProps: {
testID: 'TEST_ID_HOME',
accessibilityLabel: 'TEST_ID_HOME_ACLBL',
},
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-home' : 'ios-home-outline'}
size={26}
style={{ color: tintColor }}
/>
),
};
type MyPeopleScreenProps = {
navigation: NavigationScreenProp<*>,
};
class MyPeopleScreen extends React.Component<MyPeopleScreenProps> {
_s0: NavigationEventSubscription;
_s1: NavigationEventSubscription;
_s2: NavigationEventSubscription;
_s3: NavigationEventSubscription;
static navigationOptions = {
tabBarLabel: 'People',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-people' : 'ios-people-outline'}
size={26}
style={{ color: tintColor }}
/>
),
};
componentDidMount() {
this._s0 = this.props.navigation.addListener('willFocus', this._onEvent);
this._s1 = this.props.navigation.addListener('didFocus', this._onEvent);
this._s2 = this.props.navigation.addListener('willBlur', this._onEvent);
this._s3 = this.props.navigation.addListener('didBlur', this._onEvent);
}
componentWillUnmount() {
this._s0.remove();
this._s1.remove();
this._s2.remove();
this._s3.remove();
}
_onEvent = a => {
console.log('EVENT ON PEOPLE TAB', a.type, a);
};
render() {
const { navigation } = this.props;
return <MyNavScreen banner="People Tab" navigation={navigation} />;
}
}
type MyChatScreenProps = {
navigation: NavigationScreenProp<*>,
};
class MyChatScreen extends React.Component<MyChatScreenProps> {
_s0: NavigationEventSubscription;
_s1: NavigationEventSubscription;
_s2: NavigationEventSubscription;
_s3: NavigationEventSubscription;
static navigationOptions = {
tabBarLabel: 'Chat',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'}
size={26}
style={{ color: tintColor }}
/>
),
};
componentDidMount() {
this._s0 = this.props.navigation.addListener('willFocus', this._onEvent);
this._s1 = this.props.navigation.addListener('didFocus', this._onEvent);
this._s2 = this.props.navigation.addListener('willBlur', this._onEvent);
this._s3 = this.props.navigation.addListener('didBlur', this._onEvent);
}
componentWillUnmount() {
this._s0.remove();
this._s1.remove();
this._s2.remove();
this._s3.remove();
}
_onEvent = a => {
console.log('EVENT ON CHAT TAB', a.type, a);
};
render() {
const { navigation } = this.props;
return <MyNavScreen banner="Chat Tab" navigation={navigation} />;
}
}
const MySettingsScreen = ({ navigation }) => (
<MyNavScreen banner="Settings Tab" navigation={navigation} />
);
MySettingsScreen.navigationOptions = {
tabBarLabel: 'Settings',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-settings' : 'ios-settings-outline'}
size={26}
style={{ color: tintColor }}
/>
),
};
const SimpleTabs = TabNavigator(
{
Home: {
screen: MyHomeScreen,
path: '',
},
People: {
screen: MyPeopleScreen,
path: 'cart',
},
Chat: {
screen: MyChatScreen,
path: 'chat',
},
Settings: {
screen: MySettingsScreen,
path: 'settings',
},
},
{
lazy: true,
removeClippedSubviews: true,
tabBarOptions: {
activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff',
},
}
);
type SimpleTabsContainerProps = {
navigation: NavigationScreenProp<*>,
};
class SimpleTabsContainer extends React.Component<SimpleTabsContainerProps> {
static router = SimpleTabs.router;
_s0: NavigationEventSubscription;
_s1: NavigationEventSubscription;
_s2: NavigationEventSubscription;
_s3: NavigationEventSubscription;
componentDidMount() {
this._s0 = this.props.navigation.addListener('willFocus', this._onAction);
this._s1 = this.props.navigation.addListener('didFocus', this._onAction);
this._s2 = this.props.navigation.addListener('willBlur', this._onAction);
this._s3 = this.props.navigation.addListener('didBlur', this._onAction);
}
componentWillUnmount() {
this._s0.remove();
this._s1.remove();
this._s2.remove();
this._s3.remove();
}
_onAction = a => {
console.log('TABS EVENT', a.type, a);
};
render() {
return <SimpleTabs navigation={this.props.navigation} />;
}
}
export default SimpleTabsContainer;
For the source and other codes, look here: https://github.com/react-navigation/react-navigation/blob/master/examples/NavigationPlayground/js/SimpleTabs.js

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