React-native, react-navigation. I have a custom header, with a button, that I've extracted to another file. I can't seem to get access to the navigation object.
function SetNavOptions(title, navigation) {
const { buttonStyle, iconStyle } = styles;
return {
headerBackground: (<LinearGradient
colors={['#337ab7', '#265a88']}
style={{ flex: 1 }}
start={[0, 0]}
end={[0, 1]}
/>),
headerTitleStyle: {
color: 'white',
},
headerTitle: title,
headerLeft: (
<TouchableOpacity onPress={() => navigation.getParam('mydrawer')} style={buttonStyle}>
<View>
<Icon.FontAwesome
name='bars'
size={26}
style={iconStyle}
/>
</View>
</TouchableOpacity>
),
};
}
And this is my screen where I set the navigationOptions variable:
export default class HomeScreen extends React.Component {
static navigationOptions = SetNavOptions('Topics', this.navigation);
componentDidMount() {
this.props.navigation.setParams({ mydrawer: this.openDrawer });
}
openDrawer = () => {
this.props.navigation.navigate('myDrawer');
};
}
Running the code and clicking the button, I get 'undefined is not an object (evaluating 'navigation.getParam'). What am I doing wrong? Also, is how I defined SetNavOptions() the best way to extract all of that? I'm new to react-native.
The problem was that my static navigationOptions line was not correct in the HomeScreen class. It should be this instead:
static navigationOptions = ({ navigation }) => SetNavOptions('Topics', navigation)
Had similar problem, and route.params.mydrawer instead of navigation.getParam('mydrawer') solved the issue. Looks like there was an update with React Native router, because before it warked with navigation.getParam.
Inspired by https://stackoverflow.com/a/64932908/11127383
Related
I am using redux-react and trying to use copilot library to create a guide for my app but it is throwing me following error -
TypeError: Cannot read property 'getCurrentStep' of undefined
Below is my code:
const CustomComponent = ({ copilot }) => <View {...copilot}>
<MaterialIcon name="add" size={30} color="white" style={{marginRight : 8}} onPress={() => {
navigation.navigate("CreateSomething");
}}/>
</View>;
class Abc extends React.Component{
static navigationOptions = ({ navigation, navigationOptions }) => {
const {params} = navigation.state || {};
return {
headerTitle : <HeaderTitle title="Something"/>,
headerRight : (<View>
<CopilotStep text="Click this icon to" order={1} name="addIcon">
<CustomComponent />
</CopilotStep>
</View>)
}
};
componentDidMount(){
this.props.start()
}
}
const exportedConnect = connect (mapStateToProps, mapDispatchToProps)(copilot({
verticalOffset: 26,
animated: true,
overlay: "svg"
})(Abc));
Object.assign(exportedConnect, {
navigationOptions: Abc.navigationOptions
});
export default exportedConnect;
Am I missing something in the above code? Why is it throwing the above error?
Thank you in advance.
hi i'm working on a new react-native app, but i had some issues with the navigation from a component to a screen.
this is the link for the code on snack: https://snack.expo.io/#mimonoux/my-app-navigation-test
i have already tried this
<ButtonCarte onPress={() => this.props.navigation.navigate('Carte') } />.
but it didn't work. please if anyone could help me with this please check the snack link and take a deep look at the easy code i made for my real problem
I saw your problem now. With react-navigation,
navigation props exists in a component when : either the component is configured in your route configuration object that you defined in App.js, either you use the withNavigation HOC ( https://reactnavigation.org/docs/en/with-navigation.html ).
Now in the Medicine_listDetail component this.props.navigation does not exist since Medicine_listDetail does not appear in your route and also the props object should not be read by this.props in a functional component. You can do one of this two way :
const Medicine_listDetail = ({medicine, navigation}) => {
// i'm passing navigation props comme from parent component that have
// navigation object
// ...
}
// OR you can do
const Medicine_listDetail = (props) => {
const { medicine, navigation } = props;
// i'm passing navigation props comme from parent component that have
// navigation object
// ...
}
Hence the following is an attempt at a solution that work for me.
Medicine_listDetail component : i'm passing navigation props come from
parent component that have navigation object
...
const Medicine_listDetail = ({medicine, navigation}) => {
const {title, coordinate} = medicine;
const {
headerContentStyle,
headerTextStyle,
cityTextStyle,
addTextStyle,
infoContainerStyle,
buttonsContainerStyle,
specialityTextStyle,
buttonStyle,
textStyle
} = styles
return (
<View>
<View style={headerContentStyle}>
<Text style={headerTextStyle}>{title}</Text>
</View>
<View style={buttonsContainerStyle}>
<ButtonCarte onPress={() => navigation.navigate('Carte') }>
</ButtonCarte>
</View>
</View>
);
};
...
ButtonCarte component
const ButtonCarte = ({onPress, children}) => {
const {buttonStyle, textStyle} = styles;
return (
<TouchableOpacity onPress={() => onPress()} style={buttonStyle}>
<Ionicons name={'ios-pin'} size={20} color="white" />
<Text style={textStyle}>
Voir La Carte
</Text>
</TouchableOpacity>
);
};
Medicin component : in all_medicine() function, i'm passing navigation object in props of Medicine_listDetail component. So this is the trick.
export default class Medicin extends React.Component {
constructor(props) {
super(props);
this.state = {
list_allMedicine: data_allMedicine,
selectedIndex: 0,
};
this.updateIndex = this.updateIndex.bind(this);
}
updateIndex(selectedIndex) {
this.setState({ selectedIndex });
}
all_medicine() {
const { navigation } = this.props;
return this.state.list_allMedicine.map(medicine => (
<Medicine_listDetail key={medicine.title} medicine={medicine} navigation={navigation} />
));
}
render() {
const buttons = ['Tout', '...', '...', '...'];
const { selectedIndex } = this.state;
return (
<View style={{ flex: 1}}>
<View
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ButtonGroup
onPress={this.updateIndex}
selectedIndex={selectedIndex}
buttons={buttons}
containerStyle={{ borderRadius:8 }}
/>
</View>
<Divider
style={{
backgroundColor: 'lightgrey',
marginHorizontal: 5,
height: 2,
}}
/>
<View style={{ flex: 5 }}>
{this.state.selectedIndex == 0 ? (
<ScrollView>{this.all_medicine()}</ScrollView>
) : (
<Text>test</Text>
)}
</View>
</View>
);
}
}
At least in App.js, i change the name of carte tab from Cart to Carte because of your RootStack stack.
export default createAppContainer(
createBottomTabNavigator(
{
Home: {
screen: Home,
navigationOptions: {
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor }) => (
<Ionicons name={'ios-home'} size={25} color={tintColor} />
),
},
},
Medicin: {
screen: Medicin,
navigationOptions: {
tabBarLabel: 'Medicin',
tabBarIcon: ({ tintColor }) => (
<Image
source={require('./assets/images/Dashboard/drawable-xhdpi/doctor_heart.png')}
style={{ width: 25, height: 20, tintColor: tintColor }}
/>
),
},
},
Carte: {
screen: Carte,
navigationOptions: {
tabBarLabel: 'Carte',
tabBarIcon: ({ tintColor }) => (
<Ionicons name={'ios-map'} size={25} color={tintColor} />
),
},
},
},
{
tabBarOptions: {
activeTintColor: 'black',
inactiveTintColor: 'gray',
},
}
)
);
I test this and it work for me.
try adding this:
import { NavigationEvents, NavigationActions } from 'react-navigation';
Here is a screenshot of what's available in props in reference to the comments below:
Here is a screenshot of what I mentioned in the comments. You can see where I added a console.log. It shows in the console that although navigation is in this.props, actions within navigation is empty. I think that is the source of the problem. If you put more console.logs like the one I've done you will see where in the project it loses that information.
I am facing problem getting reference of TextInput which is defined in static navigationOptions, it's that any way to do it in React Native? i am sharing some code here so you better know about my questions.
export default class SearchClass extends React.Component {
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state;
let headerTitle = (
<View style={{flex: 1}}>
<TextInput
style={{ flex: 1, height: 30 }}
placeholder='Search'
keyboardType='default'
returnKeyType='search'
underlineColorAndroid='transparent'
onChangeText={text => params.onChangeText(text)}
onSubmitEditing={(event) => { params.onSearchButton()
}}
/>
</View>
);
return { headerTitle };
};
componentDidMount() {
this.props.navigation.setParams({
onChangeText: this.__onChangeSearchText,
onSearchButton: this.__onSearchButtonPress
});
}
__onSearchButtonPress = () => {
// clear the text of TextInput
// for that i need reference here of TextInput
this.mySearchBox.clear();
}
}
_onSearchButtonPress i need to clear the text of TextInput, for that i need reference of that TextInput which is define in navigationOptions ?
does anyone know how to do it?
I've added a static variable inside class
static textField = null;
and then assign it inside navigationOptions
<TextInput
ref={(ref) => YourClass.textField = ref}
...
then when I need to pass events (not sure if that's the best way) I either do a pubsub-js listener and publish event or I forward events - both ways explained with code bellow.
<TextInput
ref={(ref) => YourClass.textField = ref}
onChangeText={(text) => {
YourClass.textField.onChangeText(text);
// PubSub.publish(Constants.FIELD_DID_CHANGE, [text,]);
}}
then when didFocus -- viewDidAppear equivalent
let textField = YourClass.textField;
textField.onChangeText = (text) => this._textFieldDidChange(text);
or register PubSub object in componentDidMount
// this.textFieldDidChangeListener = PubSub.subscribe(Constants.FIELD_DID_CHANGE, (msg, data) => this._textFieldDidChange(data));
This is the component which contains my Drawer
export default class StackInView extends React.Component {
render() {
const Stack = StackNavigator({
DrawerStack: { screen: DrawerInView }
}, {
headerMode: 'float',
});
return (
<View style={{ flex: 1 }}>
<Stack />
</View>
);
}
}
The following is where I define my button. I want to define the button in navigationOptions of the screen, because the button should only appear on the screen with the drawer. But clicking the button doesn't work can you help me pls?
... imports ...
export default class DrawerInView extends React.Component {
static navigationOptions = {
title: "Yeah?",
headerRight: <Button title="Menu" onPress={() => {NavigationActions.navigate("DrawerOpen")}}/>
}
constructor(props) {
super(props);
}
render() {
const Drawer = DrawerNavigator({
"one": {
screen: () => {
return (<TabsInView select="something" />)
},
},
"two": {
screen: () => {
return (<TabsInView select="something else" />)
},
}
})
return (
<View style={{ flex: 1 }}>
<Drawer />
</View>
);
}
}
You can open DrawerNavigation on button click like this.
<Button title="Menu" onPress ={ ( ) => this.props.navigation.openDrawer()} />
Don't put Stack into View. It's hard to understand and you break all props.
And the reason it doesn't work is that navigationOptions in second code is not for the drawer but for the StackNavigator in the first code. So it can't execute drawer's navigation.navigate("DrawerOpen") because it's StackNavigator's.
And I highly recommend you to change your app's hierarchy. It's really hard that child Drawer passes its navigation to parent Stack's right button.
Then, it would look like this.
const MyStack = StackNavigator({
Tabs:{ screen: MyTabs, navigationOptions:(props) => ({
headerRight:
<TouchableOpacity onPress={() => {props.screenProps.myDrawerNavigation.navigate('DrawerOpen')}}>
<Text>Open Drawer</Text>
</TouchableOpacity>
})}
}
, {navigationOptions:commonNavigationOptions})
const MyDrawer = DrawerNavigator({
stack1: {
screen: ({navigation}) => <MyStack screenProps={{myDrawerNavigation:navigation}} />,
},
stack2: {
//more screen
}
})
I have two screen.
First screen is HomeScreen, second screen is ProfileScreen.
I used FlatList on HomeScreen and i wanna push to navigation to another screen. But when i used that codes, i saw that error message: "Can not read property 'navigate' of undefined"
Code like that
class ProfileScreen extends Component {
static navigationOptions = {
title: 'Profile',
};
render() {
const { navigate } = props.navigation;
return <Text>Hello, I am profile!</Text>;
}
}
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
constructor(props) {
super(props);
this.state = {
data: [],
};
}
getScreen() {
this.props.navigation.navigate('Profile')
}
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<TouchableHighlight underlayColor= 'transparent' onPress= {this.getScreen}>
<View style= {{width: 300, height: 'auto'}} >
<Text> {item.title} </Text>
<View style= {{width: 300, height: 1, backgroundColor: 'red', marginBottom: 30, marginTop: 15}} />
</View>
</TouchableHighlight>
)}
/>
</View>
);
}
}
const AppNavigator = StackNavigator({
Home: { screen: HomeScreen },
Profile: { screen: ProfileScreen }
});
You're losing the context of this in your implementation. Fix it with function call:
renderItem={({ item }) => (
<TouchableHighlight underlayColor='transparent' onPress={() => this.getScreen()}>
...
</TouchableHighlight>
)}
In addition, you can use pattern:
const { navigate } = this.props.navigation;
navigate('Profile');
Inside the constructor bind this to your getScreen method.
Simply add following line inside constructor.
this.getScreen = this.getScreen.bind(this);