How does `onChangeText={(text) => this.setState({text})` work? - javascript

On https://facebook.github.io/react-native/docs/handling-text-input they give an example of using onChangeText with:
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Type here to translate!"
onChangeText={(text) => this.setState({text})}
/>
I don't exactly understand why this.state.text is set to the value of TextInput. How exactly does it work? I understand anonymous function is used here, but still can't see where this.state.text is initiated. Can you show an example of a similar thing with plain javascript?
Thanks a lot!

but still can't see where this.state.text is initiated
it's actually initialized in the constructor
constructor(props) {
super(props);
this.state = {text: ''}; // initialized here
}
I don't exactly understand why this.state.text is set to the value of TextInput
whenever the user type text in the input, the onChangeText function is called which actually update the state => the text variable will take the value of the text input
Moreover, when you call setState it will re-render the component, so
<Text style={{padding: 10, fontSize: 42}}>
{this.state.text.split(' ').map((word) => word && '🍕').join(' ')}
</Text>
will be re-executed and you will see a 🍕 symbol for each word typed by the user

The onChangeText is listening for changes to the input. That is your text in regards to (text). We then use es6 arrow notation to call an anonymous function => . From here we call this.setState({}) , which is a built in state management function with react. It sets the variable, (which may or may not be declared) in state to the changed value of (text) to be used later or elsewhere.
The confusion might be that if you didn't declare text yourself, as such,
this.state = { text: ''}
It would be confusing how its actually storing if you didn't have text already in your state, but saw it updating. But in react, it will create a new state variable even if you didn't declare one automatically.

Only place you directly write to this.state should be the Components constructor. In other places you should be using this.setState function, which accepts an Object that is merged into Components current state.
It's possible to alter state by writing to this.state directly, However it will not lead to Component re-rendering with new data, and generally lead to state inconsistency.

this.state.text is part of the state of the component.
I don't exactly understand why this.state.text is set to the value of TextInput
It is stored in the state so that other components can access the value and use it. (For example: Form submission needs all values.)
For a TextInput form submission example :
handleSubmit = () => {
mySubmitService(this.state.textInput)
}
Another example from the react docs :
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()}; //This is the default state initialization
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2> //This is accessing the value
</div>
);
}
}
Plain JS :
var data = +(document.getElementById("TextInputId").value;);

I understand anonymous function is used here, but still can't see where this.state.text is initiated.
That's just example of code. You must specify initial state to make onChangeText work inside the constructor. FYI, that's not just an anonymous function, but an arrow function.
this.state = {
text: ''
}
Can you show an example of a similar thing with plain javascript?
I think you meant to use in other way? If so, you can use like this:
Define a method:
onChangeText(text) {
this.setState({text})
// ^^ alias of -> this.setState({text:text})
}
Use onChangeText method:
onChangeText={this.onChangeText(text)}
Bind this inside the constructor:
this.onChangeText = this.onChangeText.bind(this)

Related

How does this handleClick function in React retrieve previous state value?

This codepen toggles a button value from true to false.
I understand this apart from how this handleClick function is working:
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
Can someone please break this down and explain in simple terms how the function retrieves the isToggleOn bool value?
I know we can't directly use !this.state.isToggleOn in setState but can someone kindly explain in simple terms why this handleClick function is more reliable in this scenario for a React newbie?
This is where the handleClick function is called:
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
From the setState docs:
The first argument is an updater function with the signature:
(state, props) => stateChange
state is a reference to the component state at the time the change is being applied.
In your example prevState is state renamed, and it contains all of the assignments in state, including isToggleOn.
You could alternately just read this.state in the setState function. The prevState pattern is used to communicate that this is the old/current state, the state that is being changed.
setState docs
In this scenario you can actually use both, because it's really simple and you call the setState from the render function. In class components is a bit hard to find a place where this this.setState({oldState:this.state.oldState}) can be problematic.
On the contrary, if you use functional components or hooks, you can see this problem quite often.
JS is not Object oriented!
To understand this you have to think first that js is not an object oriented language. React tries just to simulate it. Basically when you change a state, the class containing it gets called again trying to run only the code which changes. In your case, when handleClick is called, the render function get triggered because the state has changed!
Said this, handleClick is not just a function! It's also a property of the class. React tries to understand whether it should "re-declare" those functions depending on the values they use.
Good Practice
In the case you wrote, isToggleOn: !prevState.isToggleOn, the function is indipendent. It doesn't need any other property of the class (apart of setState, which works with a different pattern), so after the state changes, the react engine doesn't have to set the function again.
Bad Practice (sometimes much much easyer to write)
On the contrary if you use isToggleOn: !this.state.isToggleOn the handleClick function is dependent of this.state so every time it changes, the react engine has to "re-declare" the function by sort of substituting this.state with {isToggleOn:...}
How to recreate that pattern
function myRandom(property){
//Kind of private Variable
let hiddenVar=Math.random();
return property(hiddenVar);
}
//Let's say here the Math class is unreachable while myRandom is.
myRandom((num)=>{
console.log(num);//This ac
})
Maybe just check the docs on this?
The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.
In essence, you are getting the previous instance of the state in the function, then making a change to the state and returning it. This new state will get saved and cause a re-render of your component.
If you use functional components, you can negate having to pass a callback here and could do something like:
function MyComponent() {
const [on, setOn] = React.useState(false)
return (
<button onClick={() => setOn(!on)}>
{on? 'ON' : 'OFF'}
</button>
);
}

How do I get inputRef to return <input> element from <Textfield>? Tried everything I can think of and it's still null

I have a TextField that I only render when another radiobutton is clicked. According to the React and Material-UI documentation, I should be able to get a ref to an input element inside a Mui TextField using inputRef={this.myRef} on the TextField. I'm able to do this for another TextField that is not toggled, but when I try that the with TextField that I just turn on, it shows null.
I've tried using inputRef={this.otherText} and inputProps={{ref: this.otherText}} and same result.
// Start of my class where I do the createRef()
class Form extends Component {
constructor(props) {
super(props);
this.otherText = React.createRef();
}
// Start of function where I try to reference the ref:
processApplication = e => {
if (e.target.value.toLowerCase() === 'other') { // this triggers the TextField to be rendered
console.log(this.otherText); // Returns null, kinda - see screenshot
}
// The TextField I'm trying to reference:
<TextField
id='applicationOther'
name='applicationOther'
label='Describe application:'
margin='normal'
multiline={true}
fullWidth={true}
onChange={this.anyChange}
autoFocus={true}
inputRef={this.otherText} // Here's where the reference is made
/>
I expect this.otherText to have a reference to the element, but this.otherText.current is null.
So to add some content to an input of any tipe, including Material TextField, you'd assign it as a value, for instance:
this.state = { input: 'Some string' }
<TextField value={this.state.input} />
Keep in mind the slight difference between uncontrolled and controlled components, so depending on your use case, you may want to pass defaultValue instead of value. From the docs:
In the React rendering lifecycle, the value attribute on form elements will override the value in the DOM. With an uncontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a defaultValue attribute instead of value.
Docs link
I have had same issue, doing following worked for me:
<TextField
variant="filled"
inputRef={(input) => {
if(input != null) {
input.focus();
}
}}
/>
The trick here was if(input != null) in my case. You can also take a look at working example here: CodeSandBox- Material-ui-TextFieldFocus

setState inside a static method

I want to set the state of one component from another component using setState method as it seems to involve less coding, however I have come to know that I can't use the this keyword in a static method which has created a problem for me. I would like to know another get around of this problem. help would really appreciated.
First component
class First extends Component {
filterByLocation(loc) {
const filteredData = this.state.passedInfo.filter(({area}) => area === loc);
this.setState({members: filteredData})
}
}
Second component
class Second extend Component {
renderSuggestion() {
<TouchableOpacity
onPress = {()=> this.setState({location}, () => {
First.filterByLocation(this.state.location);
})}>
<Text> {"Click Me"}</Text>
</TouchableOpacity>
}
}
I initially considered this a comment, but really, it's an answer:
One component should never set the state of another component, even if it had access to the component instance (and thus to setState). It certainly can't do so without access to the component instance.
Instead:
Lift state up and have it passed to the component as props, or
Use portals and again pass the state as props (note: not entirely sure portals are supported in React Native, though a search turns up projects providing similar functionlity), or
Use context (which is supported in React Native)
...or possibly any of several other things. In your specific scenario, lifting state up seems like the right thing.
Why not to pass whole this object to your method like:
<TouchableOpacity
onPress = {()=> this.setState({location}, () => {
Home.filterByLocation(this, this.state.location);
})}>
<Text> {"Click Me"}</Text>
</TouchableOpacity>
Filter method:
filterByLocation(context, loc) {
const filteredData = context.state.passedInfo.filter(({area}) => area === loc);
context.setState({members: filteredData})
}
For sure it is not a good practice, it should solve the problem but should not be overused.

React Native: this.setState is not a function

I see a number of questions on here relating to this same issue, but it seems none match the issue I'm having, and are a bit more complex.
I am in the process of learning ReactJS and React Native. I'm in the midst of reading and following the code examples from "Learning React Native" book here: https://github.com/bonniee/learning-react-native
For some reason, calling this.setState in the code below when the handleTextChange function is called, causes the "this.SetState is not a function." error. My question is why? Unlike other questions about this same issue, I don't believe my call to this.stateState is buried in a callback function or if statement. Why is it undefined?
Here is my code:
class WeatherProject extends Component {
constructor(props) {
super(props);
this.state = {
zip: "",
forecast: null
};
}
_handleTextChange(event) {
this.setState({zip: event.nativeEvent.text});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
You input {this.state.zip}.
</Text>
<TextInput
style={styles.input}
onSubmitEditing={this._handleTextChange}/>
</View>
);
}
}
Do not use bind inside a render. bind is a rather expensive operation and should only happen once. you have two options:
either bind the function in the constructor:
this._handleTextChange = this._handleTextChange.bind(this);
or use arrow function:
onSubmitEditing={(e) => this._handleTextChange(e)} />
Edit
Apparently arrow functions inside render is also a bad practice (Thx to Adam Terlson in the comments and answer below). You can read eslint docs which states:
A bind call or arrow function in a JSX prop will create a brand new function on every single render. This is bad for performance, as it will result in the garbage collector being invoked way more than is necessary.
Using arrow functions is obviously not as bad as using bind, but nevertheless should be avoided.
Regarding arrow function you also need to change _handleTextChange(event) function. Other answers didn't talk about how to change normal function to arrow function.
You need to change handler function, from:
_handleTextChange(event) {
this.setState({zip: event.nativeEvent.text});
}
To:
_handleTextChange = event => {
this.setState({zip: event.nativeEvent.text});
}
The issue is context binding, as identified in the other comments and answers here.
However, the performance of bind itself is a non-issue. The way-more-relevant issue is that using bind or arrows in your render methods creates a new function on each render, resulting in a change of props for the child that receives them, forcing a re-render.
You have two viable options:
class WeatherProject extends Component {
constructor(props) {
super(props);
this._handleTextChange = this._handleTextChange.bind(this);
}
// ...
}
Or you can use the class property notation and assign arrow functions if you're using the babel plugin for it.
class WeatherProject extends Component {
constructor(props) {
super(props);
// ...
}
handleTextChange = (event) => {
this.setState({zip: event.nativeEvent.text});
}
// ...
}
I strongly recommend you use the eslint package with the react recommended rules enabled. It will catch errors like using bind/arrows in your render, as well as tell you that underscore prefixed functions are ugly and totally not necessary in React. :)
this.setState is not a function--- Solved using this
let that = this;
that.setState({membersArray:response.data})
I had the same problem when trying to set the state. when I bind the function inside the constructor, the issue gets solved. check with below binding
constructor(props) {
super(props);
this.state = {
zip: "",
forecast: null
};
this._handleTextChange = this._handleTextChange.bind(this);
}

Having trouble with setting state to value of "Enter name" field

I have a simple TextInput for entering your name. I want to pass this to another scene. First, though, I want to save the value to the state of the current component. When I open up the debugger, I get undefined for every console.log(this.name) I have.
Here is my code:
constructor(props) {
super(props);
this.state = {
name: ''
};
}
<TextInput style={styles.inputName}
placeholder="Enter your name"
textAlign="center"
onChange={this.handleChange.bind(this)} />
handleChange(event) {
this.setState({
name: event.nativeEvent.text
});
console.log(this.name)
}
Do you know why I am always getting "undefined" as the value of name? What is the proper way to set the state equivalent to what is being typed?
Is a better approach to set state only after the submit button is pressed? If so how is that done?
You're attempting to access this.name, which is checking for a (likely nonexistent) name property on the component object in your case. You should be looking at this.state.name. However, even if you did attempt to log out the updated state in your event handler via this.state.name, you would probably still run into problems. From the React docs:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
Finally, I would suggest extracting the name value from your event through event.target.value rather than going through the nativeEvent property.

Categories