onChange in React doesn't capture the last character of text
JavascriptReactjsJavascript Problem Overview
This is my render function:
render: function() {
return <div className="input-group search-box">
<input
onChange={this.handleTextChange}
type="text"
value={this.state.text}
className="form-control search-item" />
<span className="input-group-btn"></span>
</div>
}
and I have this as my event handler:
handleTextChange: function(event) {
console.log(event.target.value);
this.setState({
text: event.target.value
});
}
The problem is that when I "save" an item, or console.log print the output, the last character is missing - for instance, if I enter "first", I'll get "firs" printed out, and there needs to be another key event to capture the last character. I've tried onKeyUp
- which doesn't let me type anything in, and I've also tried onKeyDown
and onKeyPress
, which output nothing.
What is happening here and why? and how can I get that last character to show up?
Javascript Solutions
Solution 1 - Javascript
When are you logging the state? Remember that setState
is asynchronous, so if you want to print the new state, you have to use the callback parameter. Imagine this component:
let Comp = React.createClass({
getInitialState() {
return { text: "abc" };
},
render() {
return (
<div>
<input type="text" value={this.state.text}
onChange={this.handleChange} />
<button onClick={this.printValue}>Print Value</button>
</div>
);
},
handleChange(event) {
console.log("Value from event:", event.target.value);
this.setState({
text: event.target.value
}, () => {
console.log("New state in ASYNC callback:", this.state.text);
});
console.log("New state DIRECTLY after setState:", this.state.text);
},
printValue() {
console.log("Current value:", this.state.text);
}
});
Typing a d
at the end of the input will result in the following being logged to the console:
Value from event: abcd
New state DIRECTLY after setState: abc
New state in ASYNC callback: abcd
Notice that the middle value is missing the last character. Here's a working example.
Solution 2 - Javascript
Since setState() function in asynchronous, I used await.I achieved this using async and await, here is my code
render: function() {
return <div className="input-group search-box">
<input
onChange={(e) => {this.handleTextChange(e)}}
type="text"
value={this.state.text}
className="form-control search-item" />
<span className="input-group-btn"></span>
</div>
}
The handleTextCahnge function:
handleTextChange = async function(event) {
await this.setState({text: event.target.value});
console.log(this.state.text);
}
Solution 3 - Javascript
Since React v.16.8 react hooks can be helpful. I would recommend useState AND useEffect. The Example is in React Native, however it should show how to work with the useEffect. More information about useEffect: https://reactjs.org/docs/hooks-effect.html
import React, {useState, useEffect} from 'react';
import { TextInput } from 'react-native';
export interface Props{}
const InformationInputs: React.FC<Props> = (props) => {
const [textInputs, setTextInputs] = useState("");
const handleValueChange = () => {
console.log(textInputs);
}
useEffect(() => {
handleValueChange();
}, [textInputs]);
return (
<TextInput
placeholder={'Text'}
onChangeText={(value: string) => { setTextInputs(value) }}
/>
);
};
Solution 4 - Javascript
setState() function in asynchronous. Without using callback you can use another auxiliary variable to store and use the updated value immediately. Like :
export default class My_class extends Component{
constructor(props)
{
super(props):
this.state={
text:"",
};
this.text="";
}
render: function() {
return <div className="input-group search-box">
<input
onChange={this.handleTextChange}
type="text"
value={this.state.text}
className="form-control search-item" />
<span className="input-group-btn"></span>
</div>
}
handleTextChange: function(event) {
console.log(event.target.value);
this.text = event.target.value;
this.setState({
text: event.target.value
});
}
You will get your updated value in this.text variable immediately. But you should use this.state.text to show text in your UI.
Solution 5 - Javascript
import React, { useState, useEffect } from 'react';
const MyComponent = () => {
const [userInput, setUserInput] = useState("");
const changeHandler = (e) => {
setUserInput(e.target.value);
}
useEffect(()=> {
//here you will have correct value in userInput
},[userInput])
return (
<div>
<input onChange={changeHandler} value={userInput}></input>
</div>
)
}
Solution 6 - Javascript
You are printing to the console before setting the state. Write your console log after the state is set. It will show the full text. (I had the same problem)
handleTextChange: function(event) {
**console.log(event.target.value)**
this.setState({
text: event.target.value
});
}
Solution 7 - Javascript
Since React v. 16.8 you can use react hooks.
import React, { useState } from 'react';
const MyComponent = () => {
const [userInput, setUserInput] = useState("");
const changeHandler = (e) => {
setUserInput(e.target.value);
}
return (
<div>
<input onChange={changeHandler} value={userInput}></input>
</div>
)
}
It works great for me.