Undefined _this.props.navigation.getParam in drawer custom menu - react-native

I am trying to reach token param that I am sending from the parent component:
import React, { Component } from "react";
import {
SafeAreaView,
ScrollView,
View,
Image,
Text,
ImageBackground,
} from "react-native";
import firebase from "firebase";
import CustomDrawerMenu from "./CustomDrawerMenu";
class CustomDrawer extends Component {
constructor(props) {
super(props);
this.user = firebase.auth().currentUser;
}
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<View
style={{
height: 150,
backgroudColor: "white",
}}
>
<ImageBackground
source={require("../assets/bg.jpeg")}
style={{
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center",
}}
>
<Image
source={require("../assets/profile.png")}
style={{
width: 100,
height: 100,
borderRadius: 60,
borderColor: "white",
borderWidth: 4,
}}
/>
<Text style={{ color: "white", marginTop: 10 }}>
{this.user ? this.user.email : ""}
</Text>
</ImageBackground>
</View>
<ScrollView>
<CustomDrawerMenu {...this.props} token={new Date()} />
</ScrollView>
</SafeAreaView>
);
}
}
export default CustomDrawer;
In customDrawerMenu I am trying to retrieve the token with this.props.navigation.getParam("token") but error is thrown. Here I am trying to reach it:
class CustomDrawerMenu extends Component {
constructor(props) {
super(props);
}
componentDidUpdate() {
const currToken = this.props.navigation.getParam("token");
if (currToken != this.lastToken) {
this.lastToken = currToken;
this.setState({ token: this.lastToken });
}
}
.....
}
This is the error:
this.props.navigation.getParam is not a function. (In 'this.props.navigation.getParam("token")', 'this.props.navigation.getParam' is undefined)

You passed token in props
<CustomDrawerMenu {...this.props} token={new Date()} />
So, why it should be inside this.props.navigation ?
It's available in this.props.token

Related

Make a background Image disappear from button on button click, TouchableWithoutFeedback - React Native?

I have created an extremely large button with a giraffe head and a blue sky with clouds behind. I am wondering how when you click on the giraffe head you can make the image (sky behind) disappear and then reappear when you click the giraffe again. I need lots of these buttons so I have tried to make a reusable button component.
I made a Stack of the Components.
https://snack.expo.io/#tamsynjennifer/custom-button
Otherwise this is the code:
REUSABLE BUTTON.JS
import React from 'react';
import { View, StyleSheet, Image, TouchableWithoutFeedback } from 'react-native';
const Button = ({ backgroundImage, mainImage, onPress }) => (
<View style={styles.container}>
<Image
style={styles.bgImage}
source={backgroundImage}
resizeMode={'contain'}
/>
<TouchableWithoutFeedback
onPress={onPress}
style={styles.button}
>
<Image
style={styles.image}
source={mainImage}
resizeMode={'contain'}
/>
</TouchableWithoutFeedback>
</View>
);
const styles = StyleSheet.create({
container: {
height: 250,
width: 250,
justifyContent: 'center',
alignItems: 'center',
marginTop: 0,
},
button: {
height: 200,
width: 200
},
bgImage: {
alignSelf: 'center',
justifyContent: 'center',
position: 'absolute',
height: 250,
width: 250,
},
image: {
alignSelf: 'center',
justifyContent: 'center',
position: 'absolute',
height: 200,
width: 200
},
});
export default Button;
APP.JS
import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Constants } from 'expo';
const bgImage = require("./assets/BgImage.png");
const mainImage = require("./assets/MainImage.png");
import Button from './Button';
class App extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.button}>
<Button
backgroundImage={bgImage}
mainImage={mainImage}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
height: 300,
width: 300,
justifyContent: 'center',
alignItems: 'center'
},
button: {
height: 250,
width: 250,
alignSelf: 'center',
position: 'absolute'
},
});
export default App;
This is how you can do it, I have fixed your code. Firstly you need to setState and change state onPress, so that the component re-render and change the image. Simply replace your class with this.
Expo Link
import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Constants } from 'expo';
const bgImage = require("./assets/BgImage.png");
const mainImage = require("./assets/MainImage.png");
import Button from './Button';
class App extends Component {
constructor(props){
super(props)
this.state = {
imageVisibility: true,
backgroundImagePath: require("./assets/BgImage.png")
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.button}>
<Button
backgroundImage={this.state.backgroundImagePath}
mainImage={mainImage}
onPress= {() => {this.changeImgaeVisibility()}}
/>
</View>
</View>
);
}
changeImgaeVisibility = () => {
if(this.state.imageVisibility){
this.setState({imageVisibility: false, backgroundImagePath: null})
}else{
this.setState({imageVisibility: true, backgroundImagePath: require("./assets/BgImage.png")})
}
}
}
const styles = StyleSheet.create({
container: {
height: 300,
width: 300,
justifyContent: 'center',
alignItems: 'center'
},
button: {
height: 250,
width: 250,
alignSelf: 'center',
position: 'absolute'
},
});
export default App;
You can add a function as a javascript object to your JSX to render the background image, this method will return the object that you need to render, as you can see in the example bellow:
const handleBackgroundImg = (imageBg, backgroundiImage) => {
if (imageBg === true) {
return <Image style={styles.bgImage} source={backgroundImage} resizeMode={'contain'}/>
}
return <Image />;
};
To add this function to your JSX code you have to change it like this:
const Button = ({ backgroundImage, mainImage, onPress, imageBg }) => (
<View style={styles.container}>
{handleBackgroundImg(imageBg, backgroundImage)}
<TouchableWithoutFeedback
onPress={onPress}
style={styles.button}
>
<Image
style={styles.image}
source={mainImage}
resizeMode={'contain'}
/>
</TouchableWithoutFeedback>
</View>
);
Now when you use this component you have to pass the imageBg boolean as well, considering true when you want to show the bg and false when you want to hide it. See the code below:
<Button
backgroundImage={bgImage}
mainImage={mainImage}
imageBg={true}
/>
If you have any doubt, ask here again and I can help you.

React Native Flatlist overlapping footer?

I'm getting started with React Native and writing an app. The problem I'm having a problem with the layout/styling. This is a simplified version to show my difficulty.
The Flatlist doesn't know where it's bottom is, it is overlapping the Footer component. I've messed around with it but can't get it to work properly.
import React, { Component } from 'react'
import { FlatList, StyleSheet, Text, View } from 'react-native'
class Header extends Component {
render() {
return (
<View style={styles.headerContainer}>
<Text style={styles.headerText}> </Text>
<Text style={styles.headerText}>
This is the page header!
</Text>
</View>
)
}
}
class Footer extends Component {
render() {
return (
<View style={styles.footerContainer}>
<Text style={styles.footerText}>
This is the page footer!
</Text>
</View>
)
}
}
let myData = [];
for (let i=1; i<=30; i++) {
myData.push({ key: ('My item ' + i) })
}
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Header />
<View style={styles.listWrapper}>
<FlatList
contentContainerStyle={styles.listContainer}
data={myData}
renderItem={({item}) => <Text style={styles.listItem} >{item.key}</Text>}
contentInset={{top: 0, left: 0, bottom: 50, right: 0}}
/>
</View>
<Footer />
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'pink'
},
headerContainer: {
backgroundColor: '#333',
height: 60,
},
headerText: {
textAlign: 'center',
fontSize: 20,
color: '#999'
},
listWrapper: {
flex: 1
},
listContainer: {
backgroundColor: 'lightblue',
},
listItem: {
color: 'red',
fontSize: 28
},
footerContainer: {
backgroundColor: '#333',
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
flex: 1,
height: 20
},
footerText: {
textAlign: 'center',
fontSize: 14,
color: '#999'
}
})
The only solution I have is to add the prop:
ListFooterComponent={<View style={{ height: 20 }} />}
to the Flatlist, giving it the same height as the Footer, so it would take up that space. That works, but it seems inelegant. Is there a better way to do it, like with the CSS?
Thanx.

React Native : Arranging elements

I am building a very simple app with a picker and two inputs/labels.
It currently looks like this in my iphone.
This is my code
import React from 'react';
import { StyleSheet, Text, View, Button, Modal, TextInput, Picker } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
}
state = {
b1text: 'Kg',
b2text: 'Cm',
weight: '',
height: '',
standard: 'Metric'
}
render() {
return (
<View style={styles.container}>
<Picker
selectedValue={this.state.standard}
onValueChange={(itemValue, itemIndex) => {
this.setState({standard: itemValue});
if(itemValue === "Metric") {
this.setState({b1text: "Kg"});
this.setState({b2text: "Cm"});
}
if(itemValue === "Imperial") {
this.setState({b1text: "Lbs"});
this.setState({b2text: "Inches"});
}
} }
style={{height: 100, width: 100 }}
>
<Picker.Item label="Metric" value="Metric" />
<Picker.Item label="Imperial" value="Imperial" />
</Picker>
<TextInput
style={{height: 40, width: 60, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({text: weight})}
value={this.state.weight}
/>
<Text>{this.state.b1text}</Text>
<TextInput
style={{height: 40, width: 60, borderColor: 'gray', borderWidth: 1}}
onChangeText={(text) => this.setState({text: height})}
value={this.state.height}
/>
<Text>{this.state.b2text}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
},
});
But I want it to look something like this as shown below.
I have tried margin, padding etc. Still no luck.
Can someone tell me what css/flex property I can use to change the UI like how I want ?
I've created an Expo Snack that has a closer example of the UI you want to achieve. But I'll leave it to you to work out the details.
import React from 'react';
import { StyleSheet, Text, View, TextInput, Picker } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
}
state = {
b1text: 'Kg',
b2text: 'Cm',
weight: '',
height: '',
standard: 'Metric',
};
render() {
return (
<View style={styles.container}>
<View style={styles.top}>
<Picker
selectedValue={this.state.standard}
onValueChange={itemValue => {
this.setState({ standard: itemValue });
if (itemValue === 'Metric') {
this.setState({ b1text: 'Kg' });
this.setState({ b2text: 'Cm' });
}
if (itemValue === 'Imperial') {
this.setState({ b1text: 'Lbs' });
this.setState({ b2text: 'Inches' });
}
}}>
<Picker.Item label="Metric" value="Metric" />
<Picker.Item label="Imperial" value="Imperial" />
</Picker>
</View>
<View style={styles.bottom}>
<TextInput
style={{
height: 40,
width: 60,
borderColor: 'gray',
borderWidth: 1,
}}
onChangeText={() => this.setState({ text: weight })}
value={this.state.weight}
/>
<Text>{this.state.b1text}</Text>
<TextInput
style={{
height: 40,
width: 60,
borderColor: 'gray',
borderWidth: 1,
}}
onChangeText={() => this.setState({ text: height })}
value={this.state.height}
/>
<Text>{this.state.b2text}</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
top: {
width: '100%',
flex: 1,
},
bottom: {
flex: 1,
alignItems: 'center',
},
});
One of the crucial things you need to is learn how to write styles with react-native. Here is a resource that has a guide of all of the style properties you can use with const {StyleSheet} from 'react-native'.
https://github.com/vhpoet/react-native-styling-cheat-sheet
Good luck :)

React-native elements search bar ref to use focus() method for instance is undefined

I followed the doc of the react-native-elements library but I get this.search as an undefined object...
searchbar react-native-elements
class SearchBarTest extends Component {
componentDidMount()
{
this.search.focus()//search is undefined
}
render(){
<SearchBar
ref={search => this.search = search}
...
/>
}
}
Any idea how to make this working?
EDIT:
Adding full context of the code.
The idea is having a component with a header and body. The header has an optional search bar or a title and two buttons depending on the props search.
If search props is true, then I display the searchBar.
PARENT COMPONENT:
import React, {Component} from 'react';
import {StyleSheet, View, Text, TouchableWithoutFeedback, Keyboard} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import DismissableKeyboardContainer from '../../components/DismissableKeyboardContainer';
export default class Search extends Component {
static navigationOptions = () => {
return {
tabBarIcon: ({tintColor}) => (
<Icon name="ios-search-outline" size={25} color={tintColor}/>
)
}
};
render() {
someMethod = () => {
console.log('hello');
}
return (
<DismissableKeyboardContainer search={true}>
<View style={{flex: 1, alignItems: 'center'}}>
<Text>Hello world</Text>
</View>
</DismissableKeyboardContainer>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
}
});
COMPONENT where the actual searchBar component is used:
import React, { Component} from 'react';
import { Keyboard, TouchableWithoutFeedback, TouchableOpacity, View, Text, StyleSheet} from 'react-native';
import { SearchBar } from 'react-native-elements'
import Icon from 'react-native-vector-icons/Ionicons';
import PropTypes from 'prop-types';
class DismissableKeyboardContainer extends Component {
static defaultProps = {
title: '',
search: false,
buttonLeft: '',
buttonRight: '',
borderShadow: true,
headerStyle:{}
};
componentDidMount()
{
}
render() {
let { title, search, buttonLeft, buttonRight, headerStyle, borderShadow } = this.props;
return (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
<View style={{ flex: 1 }}>
<View style={[styles.headerContainer, borderShadow ? styles.borderShadow : '', headerStyle]}>
{
!search && <View style={{position: 'absolute', padding: 10, bottom: 0, flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center'}}>
<TouchableOpacity style={{ flex: 0.2, alignItems: 'center'}}>
{
buttonLeft != "" && <Icon name={buttonLeft} size={25} color="grey" />
}
</TouchableOpacity>
<View style={{ flex: 1, alignItems: 'center' }}>
<Text style={styles.title}>{title}</Text>
</View>
<TouchableOpacity style={{ flex: 0.2, alignItems: 'center' }}>
{
buttonRight != "" && <Icon name={buttonRight} size={25} color="grey" />
}
</TouchableOpacity>
</View>
}
{
search && <SearchBar
ref={search => this.search = search}
containerStyle={{ backgroundColor: 'transparent', borderTopWidth: 0, borderBottomWidth: 0 }}
inputStyle={{ backgroundColor: 'white' }}
lightTheme
onChangeText={someMethod}
placeholder='Type Here...'
/>
}
</View>
<View style={{ flex: 1, backgroundColor: 'transparent'}}>
{this.props.children}
</View>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = StyleSheet.create({
headerContainer: {
flex: 0.12,
justifyContent: 'flex-end',
backgroundColor: 'white' ,
},
borderShadow: {
borderColor: '#ddd',
borderBottomWidth: 0,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 1,
elevation: 1,
},
title: {
fontSize: 18,
fontWeight: 'bold'
}
});
DismissableKeyboardContainer.propTypes = {
title: PropTypes.string.isRequired,
search: PropTypes.bool.isRequired,
buttonLeft: PropTypes.string.isRequired,
buttonRight: PropTypes.string.isRequired,
headerStyle: PropTypes.any,
};
export default DismissableKeyboardContainer;
I think I found the issue. As my SearchScreen is part of a TabBar from react-navigation, this.search.focus() error was appearing at the initial loading of the app. The Search screen being the second tab of the tabBar, I guess this is the reason why search couldn't be found as the screen was not active.
Found this answer: Trigger event on tabBar screen display
Which helped me to launch the this.search.focus() only when the screen Search is called under the componentDidUpdate() method
Please see code below:
1- First on my root navigator, I track react-navigation screen changes with onNavigationStateChange and screenProps
<Root
onNavigationStateChange={(prevState, currentState, action) => {
let currentScreen = action.routeName;
this.setState({ currentScreen })
}}
screenProps={{ currentScreen: this.state.currentScreen }}
/>
2- Then on my Parent component, I pass the currentScreen prop from I setup above in react-navigation
<DismissableKeyboardContainer search={true} currentScreen={this.props.screenProps.currentScreen}>
<View style={{flex: 1, alignItems: 'center'}}>
<Text>Hello world</Text>
</View>
</DismissableKeyboardContainer>
3- Now, I'm checking the screen being displayed and launch this.search.focus() only if the screen Search is displayed inside my DismissableKeyboardContainer child component.
componentDidUpdate() {
this.props.currentScreen === "Search" ? this.search.focus() : ''
}

react-native eaxmple is not working

I tried react-native 'Button' example on React Native by O'REILLY.
But it's not working correctly.
When click the button, it isn't change to 'EEK!'
It is still 'PUSH ME'.
Are there something wrong?
I added my code...
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Image,
TouchableHighlight
} from 'react-native';
class test extends Component {
// this is constructor
constructor(props) {
super(props);
this.state = { pressing: false };
}
_onPressIn = () => {
this.setState({pressing: true});
}
_onPressOut = () => {
this.setState({pressing: false});
}
// {this.state.pressing ? 'EEK!' : 'PUSH ME'} not working
render() {
return (
<View style={styles.container}>
<TouchableHighlight
onPressIn={this._onPressIn}
onPressOut={this._onPressOut}
style={styles.touchable}>
<View style={styles.button}>
<Text style={styles.welcome}>
{this.state.pressing ? 'EEK!' : 'PUSH ME'}
</Text>
</View>
</TouchableHighlight>
<Image source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}}
style={{width: 400, height: 400, resizeMode: 'contain'}} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
},
welcome : {
fontSize: 20,
textAlign: 'center',
margin: 10,
color: '#FFFFFF'
},
touchable: {
borderRadius: 100
},
button: {
backgroundColor: '#FF0000',
borderRadius: 100,
height: 200,
width: 200,
justifyContent: 'center'
},
bold: {
fontWeight: "bold"
},
italic: {
fontStyle: "italic"
}
});
class Strong extends Component {
render() {
return (
<Text style={styles.bold}>
{this.props.children}
</Text>
);
}
}
class Em extends Component {
render() {
return (
<Text style={styles.italic}>
{this.props.children}
</Text>
);
}
}
export default test;
To achieve what you want, you have to use TouchableWithoutFeedback instead of TouchableHighlight. There are no such props as onPressIn and onPressOut on TouchableHighlight, it does exist on TouchableWithoutFeedback. Here are the docs explaining it
Instead of
{this.state.pressing ? 'EEK!' : 'PUSH ME'}
Try to write
{this.state.text}
And put the text in state (don't forgot the , between pressing and text) :
text:'EEK!'
And in on pressing and out pressing, update the text just like you've done with pressing !
It should work =)