onChange in React doesn't capture the last character of text

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?


Solution 1:

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:

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:

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) }}
    />
);
};