How to bind(this) when passing a function as a prop in react native - react-native

I am working on a simple calculator app to learn react native. I have a button component that displays the number buttons. I need to pass it a function so that when the button is touched the state of the parent is updated to the new number.
Without the bind(this) it says this.setState is undefined. When I add bind(this) in the constructor all I get is a blank page in my app. My code is listed below.
constructor:
constructor() {
super();
this.state = {
total: 0,
display: 0
};
this._displayUpdate = this._displayUpdate.bind(this);
}
One row of calculator buttons:
<View style={styles.buttonRow}>
<NumButton numText={7} update={this._displayUpdate} />
<NumButton numText={8} update={this._displayUpdate} />
<NumButton numText={9} update={this._displayUpdate} />
<FuncButton funcText={'*'} />
</View>
NumButton Component:
export default class NumButton extends Component {
render() {
return (
<TouchableHighlight style={styles.buttonArea} onPress={this.props.update(this.props.numText)}>
<Text style={styles.buttonText}>{this.props.numText}</Text>
</TouchableHighlight>
)
}
}

Your parent bind is right. The problem is with the onPress of the TouchableHighlight in the NumButton component. You can't add code to execute to it, You should pass a function to it. You can use ES6 arrow function:
export default class NumButton extends Component {
render() {
return (
<TouchableHighlight style={styles.buttonArea}
onPress={()=>{ this.props.update(this.props.numText) }}
>
<Text style={styles.buttonText}>{this.props.numText}</Text>
</TouchableHighlight>
)
}
}

Related

React native changing button color not working properly

I have my reusable component for Button :
import React, { Component } from "react";
import { View, Text, TouchableOpacity } from "react-native";
import { styles } from "./styles";
class TransactionFees extends Component {
state = {
pressed: false,
};
render() {
const { pressed } = this.state;
const { speed, eth, usd } = this.props;
return (
<View>
<TouchableOpacity style={ pressed ? styles.pressedButton : null } onPress={() => this.setState({ pressed: !pressed })}>
<Text style={styles.transactionFeeTitle}>{speed}</Text>
<Text style={styles.transactionFeeSubText}>{eth} ETH</Text>
<Text style={styles.transactionFeeSubText}>$ {usd} </Text>
</TouchableOpacity>
</View>
);
}
}
export default TransactionFees;
This is how I use it in another component :
<View style={styles.transactionFeeChoices}>
<TransactionFees speed={"Slow"} eth={"0.000010"} usd={"0.02"} />
<TransactionFees speed={"Average"} eth={"0.000030"} usd={"0.03"} />
<TransactionFees speed={"Fast"} eth={"0.000050"} usd={"0.04"} />
</View>
When i press the button the background color changes to blue and the problem is When I click on second button,the first button is not going to default background color
Any solutions on how to solve this please?
The way your TransactionFees component is currently created means that each TransactionFees occurrence has its own internal state. So when one TransactionFees changes, that doesn't mean the others are automatically updated. You can see them as autonomous components.
In your case however, the TransactionFees occurrences shouldn't be automomous, a change in one occurence should reflect in the others. This is a classic react pattern where the most common solution is to "lift the state up" from the TransactionFeeds component into the parent. There even is a react article about in the official docs
Steps to take
have one state variable in the parent that keeps the selected TransactionFees
pass in pressed as a prop to the TransactionFees occurrences
pass in a change handler function that the TransactionFees component can call when one of the buttons is pressed
In code, this will be more or less:
class Manager extends Component {
constructor(props) {
super(props);
this.state = {
selected: '',
};
}
onChangeSelection = selected => {
this.setState({
selected,
});
}
render() {
const { selected } = this.state;
return (
<View style={styles.transactionFeeChoices}>
<TransactionFees speed={"Slow"} eth={"0.000010"} usd={"0.02"} pressed={selected === 'Slow'} onPress={() => this.onChangeSelection('Slow')} />
<TransactionFees speed={"Average"} eth={"0.000030"} usd={"0.03"} pressed={selected === 'Average'} onPress={() => this.onChangeSelection('Average')} />
<TransactionFees speed={"Fast"} eth={"0.000050"} usd={"0.04"} pressed={selected === 'Fast'} onPress={() => this.onChangeSelection('Fast')} />
</View>
);
}
}
class TransactionFees extends Component {
render() {
const { speed, eth, usd, pressed, onPress } = this.props;
return (
<View>
<TouchableOpacity style={ pressed ? styles.pressedButton : null } onPress={onPress}>
<Text style={styles.transactionFeeTitle}>{speed}</Text>
<Text style={styles.transactionFeeSubText}>{eth} ETH</Text>
<Text style={styles.transactionFeeSubText}>$ {usd} </Text>
</TouchableOpacity>
</View>
);
}
}
You have created 3 instances of TransactionFees and they will have their own separate state.
That's why click on the second button does not change the state of the first button.
If you want to make either of button-click trigger that style change, you need to use shared value between all those TransactionFees instances.
There can be 2 ways to do this depending on where to store that value
You can store it as a state of the parent component and pass it down to TransactionFees component
You can store it in redux store and use it inside TransactionFees by connecting the component to the store.
To do so
You need to keep the stat in the parent
And pass to each button the function to change the state
And move the current stat to each button
like this
<TransactionFees speed={"Slow"}
setClicked(()->{
this.setState({clicked:true})
clicked={this.state.clicked}
eth={"0.000010"} usd={"0.02"}
/>

How do I retrieve data from a parent in react native using Flatlist?

I have a flatlist in ResultsShowScreen.js and I'm trying to pass some data to a child screen when a user taps on an item in the flatlist.
ResultsShowScreen.js
<TouchableOpacity
onPress = {() => navigation.navigate('Audioplayer', {id: item.id, name:item.name, audio_file: item.audio_file}) } >
<Text> {item.name} ({item.length} min) </Text>
<Text> {item.short_desc} </Text>
<Text> {item.long_desc} </Text>
<Text> Avg Rating: {item.avg_rating}/5 ({item.num_ratings} ratiings) </Text>
</TouchableOpacity>
In AudioplayerScreen.js, I currently have the below and this works.
const AudioplayerScreen = ( {navigation} ) => {
// Get values from previous page
const name = navigation.getParam('name');
const id = navigation.getParam('id');
const audio_file = navigation.getParam('audio_file');
But I'm trying to follow the tutorial here (https://amanhimself.dev/build-an-audio-player-in-react-native) by having the component in the format of
export default class AudioplayerScreen extends React.Component {
// rest of code here
In AudioplayerScreen.js, how do I retrieve the data being passed from the parent using the above?
You can access the same navigation prop like so this.props.navigation. react-navigation makes sure that prop is passed when you register the screen.
EDIT
Here's a snippet of what that would look like:
export default class AudioplayerScreen extends React.Component {
constructor(props){
super(props);
this.name = props.navigation.getParam('name');
}
...
// or outside constructor
return <Button title={this.props.navigation.getParam('name')} ... />
}

undefined is not a function in TouchableOpacity onPress

The question is almost similar to this one :
touchableopacity onpress function undefined (is not a function) React Native
But the problem is, I am getting the error despite the fact that I have bind the function. Here is my TouchableOpacity component:
<TouchableOpacity style={styles.eachChannelViewStyle} onPress={() => this.setModalVisible(true)}>
{item.item.thumbnail ?
<Image style={styles.everyVideoChannelThumbnailStyle} source={{uri: item.item.thumbnail}} />
: <ActivityIndicator style= {styles.loadingButton} size="large" color="#0000ff" />}
<Text numberOfLines={2} style={styles.everyVideoChannelVideoNameStyle}>
{item.item.title}
</Text>
</TouchableOpacity>
And this is my setModalVisible function:
setModalVisible(visible) {
console.error(" I am in set modal section ")
this.setState({youtubeModalVisible: visible});
}
Also, I have bind the function in constructor as follows:
this.setModalVisible = this.setModalVisible.bind(this);
But, I am still getting same error that undefined is not a function. Any help regarding this error?
The render method and your custom method must be under the same scope. In code below I have demonstrated the same. I hope you will modify your code accordingly as I assume you got the gist :)
class Demo extends Component {
onButtonPress() {
console.log("click");
}
render() {
return (
<View>
<TouchableOpacity onPress={this.onButtonPress.bind(this)}>
<Text> Click Me </Text>
</TouchableOpacity >
<View>
);
}
}
Alternatively binding method in constructor will also work
class Demo extends Component {
constructor(props){
super(props);
this.onButtonPress= this.onButtonPress.bind(this);
}
onButtonPress() {
console.log("click");
}
render() {
return (
<View>
<TouchableOpacity onPress={this.onButtonPress()}>
<Text> Click Me </Text>
</TouchableOpacity >
<View>
);
}
}
I'm not sure if this will help but I write my functions this way and haven't encountered this problem.
If I were you I'd try binding the function in the place where you declare it.
setModalVisible = (visible) => {
this.setState({ youtubeModalVisible: visible });
}
If you do this, you don't have to bind in the constructor.
constructor(props) {
...
// Comment this out to see it will still bind.
// this.setModalVisible = this.setModalVisible.bind(this);
...
}
Lastly, if this function will only set the modal's state to visible, you might want to remove the argument and pass it this way.
<TouchableOpacity style={styles.eachChannelViewStyle} onPress={this.setModalVisible}>
...
</TouchableOpacity>
// Refactored function declaration would look like this
setModalVisible = () => {
this.setState({ youtubeModalVisible: true });
}

React Native - Push one Screen To Another have issue

I used the 'StackNavigator', I have issue to push when navigation code write in button event. but its working fine if we directly code onPress event,
Import file.
import { StackNavigator } from "react-navigation";
import SignUpScreen from "./SignUp";
Push another is working :
render() {
console.disableYellowBox = true;
const { navigate } = this.props.navigation;
return (
<View style={styles.viewStyle}>
<TouchableHighlight style = {styles.buttonStart}
onPress={() => navigate("SignUpCompany")}>
<Image
source={require('./Images/hire.png')}
/>
</TouchableHighlight>
</View>
);
}
Push another via function is not working :
pushcode() {
console.log('call');
this.props.navigation.navigate('SignUp');
}
render() {
return (
<TouchableHighlight style = {styles.buttonStart}
onPress={this.pushcode}>
<Image
source={require('./Images/hire.png')}
/>
</TouchableHighlight>
);}
ERROR ON CLICK BUTTON :
Thanks. Please help me.
Look like you are using the push extra in this line
this.props.navigation.navigate.push('SignUp');
Try this will work for you
this.props.navigation.navigate('SignUp');
May be this can help you
I think you miss the function bind within constructor --- that's why you got undefined is not an object (evaluating 'this.props.navigation'). because this is undefined inside pushcode function scope.
add below into your constructor:
constructor(props) {
super(props);
this.pushcode = this.pushcode.bind(this);
...
}

undefined is not an object(evaluating 'this.props._navigate.replace/push')

I'm new to React Native and trying ( followed a tutorial ) to move to another screen when a button is clicked, this is the updated code I have and a photo of the error I get when pressing the button.
index.ios.js:
export default class Application extends Component {
constructor(){
super();
}
renderScene(route, navigator){
if(route.name == 'login'){
return <Login navigator={navigator}/>
}else if(route.name == 'home'){
return <Home navigator={navigator}/>
}
}
render() {
return <Navigator
initialRoute={{name: 'login'}}
renderScene={this.renderScene.bind(this)}/>
}
}
LoginForm:
export default class LoginForm extends Component{
constructor(props){
super(props);
this.navigate = this.navigate.bind(this);
}
navigate(routeName){
this.props.navigator.replace({
name: routeName,
Component: Home
});
}
render(){
return(
<View style={mStyle.container}>
<StatusBar barStyle='light-content'/>
<TouchableOpacity>
<Text style={mStyle.btnText}>
LOGIN WITH FACEBOOK
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>this.navigate('home')}>
<Text style={mStyle.noLoginTxt}>
CONTINUE WITHOUT LOGIN
</Text>
</TouchableOpacity>
</View>
);
}
}
I'm trying to move from login screen to home screen when button is clicked.
I get this problem when pressing the button on the emulator:
A couple of problems that you are facing.
1.- The syntax is:
this.props.navigator.{any_method}
Such as:
this.props.navigator.push
this.props.navigator.replace
Not
this.props._navigate.{any_method}
2.- If you have child views make sure you are sending to the childs the Navigator property such as.
<View>
<MyChildComponent navigator={this.props.navigator} />
</View>
3.- Make sure your child view gets the props from the parent on the constructor.
Change
constructor(){
super();
this._navigate = this._navigate.bind(this);
}
To
constructor( props ){
super( props );
// The following line is not required since you are using an arrow function to call this one.
// this._navigate = this._navigate.bind(this);
}
It is because there is a mismatch between property you use in Navigator and in your component
Change
_navigate(name){
this.props._navigate.replace({ //I also tried push, same problem.
name
});
}
to
_navigate(name){
this.props.navigator.replace({ //I also tried push, same problem.
name
});
}