I don't understand the reaction of my onPress - javascript

I created a short test to get an explanation of how my code reacts.
Basically I want to call a function only when I press my button.
No problem, the code below works.
// Components/Test.js
import React, { useState } from "react";
import { View, StyleSheet, TextInput, Button } from "react-native";
function Test(props) {
const [text, setText] = useState("");
const myFunction = () => {
console.log("test");
};
return (
<View style={styles.container}>
<TextInput
style={{ borderWidth: 1, width: "70%" }}
onChangeText={(text) => setText(text)}
/>
<Button title="Button" onPress={myFunction} />
</View>
);
}
// Styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
export default Test;
As I'm learning Javascript at the same time, I thought I'd put parentheses after my function. Technically, if I want to pass parameters to my function, I thought I should do it like this.
<Button title="Button" onPress={myFunction()} />
And now with this change, my "myFunction" is called systematically if I modify the text in TextInput.
Can someone explain what is going on? Thank you !

You should use arrow function
<Button title="Button" onPress={() => myFunction('parameter')} />
if you just call like
<Button title="Button" onPress={myFunction()} /> // its calling function directly
Its call the function directly

Arrow Functions:
We all love them, but should be cautious when using them with React. Arrow functions are great, they let us quickly create new javascript function short hand. The consequence of this is exactly that, it creates a new function every time it is executed. For the most part in Javascript this isn’t a real issue. When it comes to the React render method this can quickly become an issue.
render(){
return (
<View>
<TouchableOpacity onPress={() => console.log('Hello!')}>
Click Me
</TouchableOpacity>
</View>
)
}
So what could be the harm here, the button is simply just logging out a string on press. But in fact, when React executes this render method it will always see a new function. The arrow function returns a new function every time. This causes React to think something has changed in our view, when in fact nothing has.
logHello(){
console.log('Hello!');
}
render(){
return (
<View>
<TouchableOpacity onPress={this.logHello}>
Click Me
</TouchableOpacity>
</View>
)
}

Everytime you change the text, the state of the component also changes becuase, you've used onChangeText={(text) => setText(text)}, this causes your component to re-render.
During re-render, the following line is also executed. In this line you are calling myFunction.
<Button title="Button" onPress={myFunction()} />
In short, changing the text causes state update, which in turn causes a re-render, and during that re-render myFunction is called.

Related

Pushing to array removes old data (React Native)

I'm trying to push text input into an array. When I modify the input text and push it, the array is wiped before the push (not showing my previous pushes in the console.log). What am I missing?
const [text, onChangeText] = React.useState('Useless Text');
let chatHistory = [];
function logHistory(text) {
chatHistory.push(text);
console.log(chatHistory);
}
return (
<View style={styles.container}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<TextInput
style={styles.input}
onChangeText={onChangeText}
value={text}
/>
<Button title={'Ask the computer...'} onPress={() => logHistory(text)} />
</View>
</View>
);
I supposed that because you change state, it rerender this component and redeclare chatHistory as empty array.
use useState for chatHistory instead.
And instead of Array.push i recommend to use chatHistory = [...chatHistory, text],in useState case setChatHistory([...chatHistory,text]);
If you want the changes in chatHistory to be reflected on your component you need to use a state and not just a plain variable. Such variables do not survive through re-renders and anything you store in regular variables will be lost. Try out after making these changes
const [chatHistory, setChatHistory] = React.useState([]);
function logHistory(text) {
setChatHistory((history) => [...history, text]);
}
Also the console.log statement might not give you the result you are expecting. You might also want to display the chat history somewhere maybe in a flat list.

react native display input on real time

I have a Text Input in React Native and I want to display the typed input on real time (two way binding ) in a way that when typing each letter in the input, the text under the input field is automatically updated with the letter typed. I want to achieve this without the use of state but this code doesn't work
export default function App() {
const updateDisplay=(typedLetters)=> {return (<View><Text>typedLetters</Text></View>)}
return(
<TextInput
style={{height: 40,margin:20}}
placeholder="Search"
onChangeText={(text)=>updateDisplay(text)}
/>)
}
First, updateDisplay should be like this:
const updateDisplay = (typedLetters) => {
return (
<View>
// notice argument is in {}
<Text>{typedLetters}</Text>
</View>
);
};
In order to show the text, you have to call the updateDisplay inside the component:
return (
<View>
<TextInput
style={{ height: 40, margin: 20 }}
placeholder="Search"
onChangeText={(text) => updateDisplay(text)}
/>
{/* what parameter you are going to be passing to this function */}
{updateDisplay()}
</View>
The thing is when you defined the updateDisplay, it receives an argument. So somehow you need to extract the input value of TextInput component. That is why we need to use the state.
TextInput is actually a function and you cannot go inside of a function and grab a value. Inside a function, you mutate the state. we use setState because we are not setting the state, we are asking React and it decides when to set it.
export default function App() {
const [text, setText] = useState(null);
const updateDisplay = (typedLetters) => {
return (
<View>
<Text>{typedLetters}</Text>
</View>
);
};
return (
<View>
<TextInput
style={{ height: 40, margin: 20 }}
placeholder="Search"
// now save the input value to state.
onChangeText={(text) => setText(text)}
/>
{/* what parameter you are going to be passing to this function */}
{updateDisplay(text)}
</View>
);
}
It is not possible to do this without state. Using state you provide a hook for the UI to know when to re-render your component. Without it, your UI will not re-render, and you won't see any difference.

Can't find variable when setting value to selected image in React Native

Here are my functions. They check the selected image by the user and assign an ID to them.
setCharacter = (props) => {
this.setState({
character:props
})
}
const pressHandler = (character) => {
this.setCharacter(character)
console.log(character);
}
I'm using the functions here to assign the ID.
TouchableOpacity onPress={(pressHandler('1'))}>
<Image style={{height: 120,
width: 120, alignSelf: 'center'}} source={require('../assets/characters/001-superhero.png')}
/>
</TouchableOpacity>
<TouchableOpacity onPress={(pressHandler('2'))}>
<Image style={{height: 120,
width: 120, alignSelf: 'center'}} source={require('../assets/characters/003-superhero.png')}
/>
</TouchableOpacity>
This is my error.
ReferenceError: Can't find variable: setCharacter
Thank you very much in advance! I used another stack overflow link to create this:
How to get value of text in state.when i clicked on TouchableOpacity in react-native?
I hope I'm following all the rules on stackoverflow :)
Try using like
const setCharacter = (props) => {
// .. use state from `useState` for hooks
}
const pressHandler = (character) => {
setCharacter(character)
console.log(character);
}
TouchableOpacity onPress={() => pressHandler('1')}>
...
</TouchableOpacity>
<TouchableOpacity onPress={() => pressHandler('2')}>
...
</TouchableOpacity>
Three things:
What Nooruddin said -- onPress has to point at a function and (pressHandler('1') is not a function. Change it to onPress={()=>pressHandler('1')}
You need to declare setCharacter properly, with const or something similar: const setCharacter = ...
Try replacing this.setCharacter(character) with setCharacter(character) -- I can't tell for sure if that's an issue but it may be.
You can use Bind or an arrow function but the below should work.
In your constructor also init the state with the character property.
constructor(props) {
super(props);
this.state = {
character: '',
};
}
<TouchableOpacity onPress={this.pressHandler.bind(this, '1')}>
Then you can just update your state with whatever value you passed from the button press in your case either a 1 or a 2.
const pressHandler = (c) => {
this.setState({character: c});
console.log(c);
}
To use the character that was passed in you would just access it via this.state.character
<Text>Current Image ID: {this.state.character}</Text>

Problem with onChangeText whenn using react-native-component this.setState doesnt set state inside onChangeText

I am having a problem with react native and am kinda stuck on to why this isn't working. I am trying to update my state a friends page using the onChangeText property of the input component from react-native-elements.
export default class FriendsPage extends Component {
constructor(props) {
super(props);
this.state = {
search:'',
loading:false
};
}
findFriend(){
//Does stuff
}
renderButtonOrLoading() {
if(this.state.loading){
return <Text>Loading</Text>
}
return(
<View style={styles.container}>
<Button title = "Search" onPress={()=>this.findFriend()} styles={styles.button}/>
</View>
);
}
render(){
console.log(this.search)
return(
<View style={styles.container}>
<TextInput style={styles.input}
placeholder='username'
onChangeText={search =>this.setState({ search})}/>
{this.renderButtonOrLoading()}
</View>
);
}
}
const styles = StyleSheet.create({
container:{
marginTop: 100,
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}
});
My problem is that every time that I call the function findFriends(), I get an error that sais that this.search is undefined. I tried to console log this.search during render and it just seems to stay at undefined each render cycle. I have a feeling I should be using props somehow but am not sure as I am relatively new to react.
Edit:
Thanks for all the answers, I was trying to call this.search although it is supposed to be this.state.search and this is what broke my code.
It is unclear if the variable search is the same as the one in the state. If it is, this code will not work.
onChangeText={search =>this.setState({ search})} // setting a state to itself won't work
If it is a separate variable, you have called setState incorrectly. I recommend changing the name of the variable. The setState call should look more like this:
onChangeText={ s => this.setState( { search: s } );
search is undefined (falsey) because you set it to ' ' in your state. For a class-based React apporach, you can use the getDerivedStateFromProps() method (if you are passing a search term in from the props).

React Native APIs - why use destructuring with just one parameter?

I'm studying JavaScript, React Native, and React Navigation. I have just learned about destructuring. It's great, except I don't see the point when there is only one parameter. For example, why not just write
function HomeScreen( navigation ) { /* same code as below */
instead of
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
Thanks for any insights
Typically functional components will take a props object as an agrument. props might have any number of key value pairs. For example, you might have props.navigation. But writing function HomeScreen( navigation ) is not the same as writing function HomeScreen({ navigation }). Writing { navigation } instead of props in the argument desctuctures the value from its parent object. If you wanted to avoid destructuring, you would write it like this:
function HomeScreen( props ) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => props.navigation.navigate('Details')}
/>
</View>
);
}
Again, a react functional component expects to recieve the props object as the argument. We usually call it props, but it could be called anything. This next block is identical, we're just naming props something else:
function HomeScreen( swissArmyKnife ) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => swissArmyKnife .navigation.navigate('Details')}
/>
</View>
);
}
For just one parameter, you're right, it's probably not necessary to destructure. Its just a shorthand. But when you have a props object with many many parameters, and you want to pull only certain values from it, it makes more sense. like lets say your props object had parameters navigation, trains, busses, airplanes, bikes, skateboards, and you just want to use a few in your component:
function HomeScreen( {busses, bikes} ) {
// do some stuff here with busses and bikes, don't worry about the whole props object
}
The first argument passed to a React component is the props object made up of all the attributes in the JSX.
<HomeScreen navigation={...} />
It's going to be an object no matter what.
Destructuring is the conventional way to show which props you care about in your component. Some linters will take advantage of that and alert discrepancies between the JSX and the component.

Categories

Resources