I'm new to React Native development and I'm struggling to understand why does the argument that I pass on a function like <TouchableHighlight onPress={props.executeFunction(props.label)}>
is a SyntheticBaseEvent, instead of the props.label itself
On App.js:37 console.log, it shows SyntheticBaseEvent {_reactName: 'onClick', _targetInst: null, type: 'click', nativeEvent: PointerEvent, target:
Am I losing reference to the original functions?
App.js:
import { StyleSheet, Text, View } from 'react-native';
import React, { Component } from 'react'
import Calculator from './Calculator';
import { LinearGradient } from 'expo-linear-gradient';
const initialState = {
displayValue: '0',
clearDisplay: false,
previousOperation: null,
operation: null,
values: [null, null],
currentPositionOnValues: 0,
originalValue: 0
}
class App extends Component {
constructor(props) {
super(props);
this.state = { ...initialState }
this.clear = this.clear.bind(this);
this.addDigit = this.addDigit.bind(this);
}
clear() {
this.setState({ ...initialState });
}
addDigit(digit) {
console.log(digit)
if (digit === "." && this.state.displayValue.includes('.')) {
// Prevent double decimals
return
}
const clearDisplay = this.state.displayValue === '0' || this.state.clearDisplay
/*
Boolean value saying if it's necessary to clear the display
True if the currentValue display value is 0 or the variable this.state.clearDisplay is set to true
*/
const currentValue = clearDisplay ? '' : this.state.displayValue
/*
currentValue shows the 'cleared' value or the display value
*/
const displayValue = currentValue digit
this.setState({ displayValue: displayValue, clearDisplay: false })
if (digit !== '.') {
const i = this.state.currentPositionOnValues
const newValue = parseFloat(displayValue)
const values = [...this.state.values]
values[i] = newValue
this.setState({ values: values })
}
}
render() {
return (
<View style={styles.container}>
<LinearGradient
colors={['#4b6cb7', '#182848']}
style={styles.background}
start={[1, 1]} end={[0, 0]}
>
</LinearGradient>
<Text style={styles.head}>Calculator</Text>
<Calculator
addDigit={() => this.addDigit}
clear={() => this.clear}
setOperation={() => this.setOperation}
displayValue = {this.state.displayValue}
/>
</View>
);
}
}
export default App;
Calculator.js
import React from 'react';
import { View } from 'react-native';
import Interface from './Interface';
const Calculator = (props) => {
return (
<View>
<Interface
addDigit={props.addDigit}
clear={props.clear}
displayValue={props.displayValue}
setOperation={props.setOperation}
/>
</View>
);
}
export default Calculator;
Interface.js
import React from 'react';
import { View, StyleSheet, FlatList, Text } from 'react-native';
import Button from './Button';
import Display from './Display';
const Interface = (props) => {
return (
<View style={style.container}>
<Display value={props.displayValue} />
<Button label="AC" executeFunction={props.clear} triple />
<Button label="/" executeFunction={props.setOperation} operation />
<Button label="7" executeFunction={props.addDigit} />
<Button label="8" executeFunction={props.addDigit} />
<Button label="9" executeFunction={props.addDigit} />
<Button label="*" executeFunction={props.setOperation} operation />
<Button label="4" executeFunction={props.addDigit} />
<Button label="5" executeFunction={props.addDigit} />
<Button label="6" executeFunction={props.addDigit} />
<Button label="-" executeFunction={props.setOperation} operation />
<Button label="1" executeFunction={props.addDigit} />
<Button label="2" executeFunction={props.addDigit} />
<Button label="3" executeFunction={props.addDigit} />
<Button label=" " executeFunction={props.setOperation} operation />
<Button label="0" executeFunction={props.addDigit} double />
<Button label="." executeFunction={props.addDigit} />
<Button label="=" executeFunction={props.setOperation} operation />
</View>
);
}
const style = StyleSheet.create({
container: {
width: 400,
borderRadius: 5,
overflow: 'hidden',
position: 'relative',
justifyContent: 'center',
flexDirection: 'row',
flexWrap: 'wrap',
}
})
export default Interface
Button.js
import React from "react";
import { View, StyleSheet, Text, TouchableHighlight } from "react-native";
const Button = (props) => {
let classes = 'button '
classes = props.operation ? 'operation' : ''
classes = props.double ? 'double' : ''
classes = props.triple ? 'triple' : ''
if (props.operation) {
return (
<TouchableHighlight onPress={props.executeFunction(props.label)}>
<View style={[style.button, style.operation]}>
<Text style={style.text}>
{props.label}
</Text>
</View>
</TouchableHighlight>
)
} else if (props.double) {
return (
<TouchableHighlight onPress={props.executeFunction(props.label)}>
<View style={[style.button, style.double]}>
<Text style={style.text}>
{props.label}
</Text>
</View>
</TouchableHighlight>
)
} else if (props.triple) {
return (
<TouchableHighlight onPress={props.executeFunction()}>
<View style={[style.button, style.triple]}>
<Text style={style.text}>
{props.label}
</Text>
</View>
</TouchableHighlight>
)
} else {
return (
<TouchableHighlight onPress={props.executeFunction(props.label)} activeOpacity={0.8}>
<View style={[style.button]}>
<Text style={style.text}>
{props.label}
</Text>
</View>
</TouchableHighlight>
)
}
}
const style = StyleSheet.create({
button: {
width: 100,
height: 100,
fontSize: 30,
backgroundColor: '#f0f0f0',
outline: 'none',
textAlign: 'center',
justifyContent: 'space-evenly',
borderColor: '#888',
borderWidth: 1,
borderStyle: 'solid',
},
double: {
width: 200,
},
triple: {
width: 300,
},
operation: {
backgroundColor: '#fa8231',
color: '#FFF',
},
text: {
fontSize: 30,
textAlign: 'center',
justifyContent: 'space-evenly',
}
})
export default Button
CodePudding user response:
What is happening?
Currently your code will execute props.executeFunction(props.label)
once when the component loads.
This is because the function is immediately invoked when evaluated:
// directly invokes `props.executeFunction` with `props.label` because of brackets `()`
onPress={props.executeFunction(props.label)}
Because your function returns undefined
, this is what subsequently gets passed to the onPress
prop.
What's the solution?
If you want to pass props.label
then you can pass via an anonymous function instead:
onPress={() => props.executeFunction(props.label)}
The onPress
prop will attach the anonymous function to the event. When the event is triggered, the function will be called which will in turn call props.executeFunction
passing props.label
as desired.
The SyntheticEvent
The onPress
function passes a SyntheticEvent
when calling the handler. We can see this by logging the event in an anonymous function:
onPress={(event) => console.log(event)} // SyntheticEvent
Therefore, if you pass props.executeFunction
as a direct argument to the onPress
prop this event is what gets passed:
onPress={props.executeFunction} // passes the event as the argument to `props.executeFunction`
Examples
Invoking inline
This executes the function when the expression is evaluated (once). undefined
is returned and passed to onClick
so the onClick
handler is not added.
function App(props) {
function foo(bar) {
console.log(bar);
}
return (
<button onClick={foo("test")}>Test</button>
);
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Invoking via anonymous function
The onClick
handler is passed the anonymous function which will execute on every click.
This is useful if you need to pass arguments to your functions.
function App(props) {
function foo(bar) {
console.log(bar);
}
return (
<button onClick={() => foo("test")}>Test</button>
);
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Passing function as an argument
The event gets passed to the function.
This is useful if you need to access the event.
function App(props) {
function foo(bar) {
console.log(bar); // this logs the event as the event is passed
}
return (
<button onClick={foo}>Test</button>
);
}
ReactDOM.render(
<App />,
document.getElementById("react-root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>
CodePudding user response:
updated your code: https://snack.expo.dev/qw9iqqVxs
App.JS
import { StyleSheet, Text, View } from 'react-native';
import React, { Component } from 'react';
import Calculator from './Calculator';
import { LinearGradient } from 'expo-linear-gradient';
const initialState = {
displayValue: 0,
clearDisplay: false,
previousOperation: null,
operation: null,
values: [null, null],
currentPositionOnValues: 0,
originalValue: 0,
};
class App extends Component {
constructor(props) {
super(props);
this.state = { ...initialState };
this.clear = this.clear.bind(this);
this.addDigit = this.addDigit.bind(this);
}
clear() {
this.setState({ ...initialState });
};
addDigit(digit){
if (digit === '.' && this.state.displayValue.includes('.')) {
// Prevent double decimals
return;
}
const clearDisplay =
this.state.displayValue === '0' || this.state.clearDisplay;
/*
Boolean value saying if it's necessary to clear the display
True if the currentValue display value is 0 or the variable this.state.clearDisplay is set to true
*/
const currentValue = clearDisplay ? '' : this.state.displayValue;
/*
currentValue shows the 'cleared' value or the display value
*/
const displayValue = currentValue digit;
this.setState({ displayValue: displayValue, clearDisplay: false })
if (digit !== '.') {
const i = this.state.currentPositionOnValues;
const newValue = parseFloat(displayValue)
const values = [...this.state.values];
values[i] = newValue;
this.setState({ values: values });
}
};
render() {
return (
<View style={styles.container}>
<LinearGradient
colors={['#4b6cb7', '#182848']}
style={styles.background}
start={[1, 1]}
end={[0, 0]}></LinearGradient>
<Text style={styles.head}>Calculator</Text>
<Text style={styles.head}>
{JSON.stringify(this.state.displayValue)}
</Text>
<Calculator
addDigit={(digit) => {
this.addDigit(digit);
}}
clear={(value) => {
this.clear(value)
}}
setOperation={this.setOperation}
displayValue={this.state.displayValue}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
background: {
background: 'red',
},
triple: {
width: 300,
},
operation: {
backgroundColor: '#fa8231',
color: '#FFF',
},
text: {
fontSize: 30,
textAlign: 'center',
justifyContent: 'space-evenly',
},
});
export default App;
Button.JS
import React from "react";
import { View, StyleSheet, Text, TouchableHighlight } from "react-native";
const Button = ({operation, double, triple, executeFunction, label}) => {
let classes = 'button '
classes = operation ? 'operation' : ''
classes = double ? 'double' : ''
classes = triple ? 'triple' : ''
if (operation) {
return (
<TouchableHighlight onPress={()=>executeFunction(label)}>
<View style={[style.button, style.operation]}>
<Text style={style.text}>
{label}
</Text>
</View>
</TouchableHighlight>
)
} else if (double) {
return (
<TouchableHighlight onPress={()=>executeFunction(label)}>
<View style={[style.button, style.double]}>
<Text style={style.text}>
{label}
</Text>
</View>
</TouchableHighlight>
)
} else if (triple) {
return (
<TouchableHighlight onPress={()=>executeFunction()}>
<View style={[style.button, style.triple]}>
<Text style={style.text}>
{label}
</Text>
</View>
</TouchableHighlight>
)
} else {
return (
<TouchableHighlight onPress={()=>executeFunction(label)} activeOpacity={0.8}>
<View style={[style.button]}>
<Text style={style.text}>
{label}
</Text>
</View>
</TouchableHighlight>
)
}
}
const style = StyleSheet.create({
button: {
width: 100,
height: 100,
fontSize: 30,
backgroundColor: '#f0f0f0',
outline: 'none',
textAlign: 'center',
justifyContent: 'space-evenly',
borderColor: '#888',
borderWidth: 1,
borderStyle: 'solid',
},
double: {
width: 200,
},
triple: {
width: 300,
},
operation: {
backgroundColor: '#fa8231',
color: '#FFF',
},
text: {
fontSize: 30,
textAlign: 'center',
justifyContent: 'space-evenly',
}
})
export default Button
Calculator
import React from 'react';
import { View } from 'react-native';
import Interface from './Interface';
const Calculator = ({addDigit, clear, displayValue, setOperation}) => {
return (
<View>
<Interface
addDigit={addDigit}
clear={clear}
displayValue={displayValue}
setOperation={setOperation}
/>
</View>
);
}
export default Calculator;
Interface.JS
import React from 'react';
import { View, StyleSheet, FlatList, Text } from 'react-native';
import Button from './Button';
const Interface = ({clear, addDigit, setOperation}) => {
return (
<View style={style.container}>
<Button label="AC" executeFunction={clear} triple />
<Button label="/" executeFunction={setOperation} operation />
<Button label="7" executeFunction={addDigit} />
<Button label="8" executeFunction={addDigit} />
<Button label="9" executeFunction={addDigit} />
<Button label="*" executeFunction={setOperation} operation />
<Button label="4" executeFunction={addDigit} />
<Button label="5" executeFunction={addDigit} />
<Button label="6" executeFunction={addDigit} />
<Button label="-" executeFunction={setOperation} operation />
<Button label="1" executeFunction={addDigit} />
<Button label="2" executeFunction={addDigit} />
<Button label="3" executeFunction={addDigit} />
<Button label=" " executeFunction={setOperation} operation />
<Button label="0" executeFunction={addDigit} double />
<Button label="." executeFunction={addDigit} />
<Button label="=" executeFunction={setOperation} operation />
</View>
);
}
const style = StyleSheet.create({
container: {
width: 400,
borderRadius: 5,
overflow: 'hidden',
position: 'relative',
justifyContent: 'center',
flexDirection: 'row',
flexWrap: 'wrap',
}
})
export default Interface