76

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?

reectrix
  • 7,999
  • 20
  • 53
  • 81
  • seems almost exactly like the example: https://facebook.github.io/react/docs/forms.html - anything else modifying that scope? – pherris Oct 12 '15 at 19:25
  • what other react-functions does your component have? what's in `getInitialState()`? – wintvelt Oct 12 '15 at 21:08
  • How are you saving or `console.log`ing it exactly? Remember that `setState` is async: http://stackoverflow.com/questions/32674174/onchange-function-console-log-is-one-character-short – Michelle Tilley Oct 12 '15 at 21:58
  • I had the same problem. I was setting state from event.target.value, and then assigned the new state to a data store. The last character didn't make it into the store. Instead I assigned event.target.value to both the state and the store. The last character makes it into the data store now. – luke_f Dec 11 '15 at 01:09

7 Answers7

71

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.

Michelle Tilley
  • 157,729
  • 40
  • 374
  • 311
10

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);
   }
Aman
  • 339
  • 3
  • 12
  • 1
    async await should be working, but it did not for me in a Function Component _state hooks_. Then what I did was, called a function `onBlur` and checked the value. Hope this would help someone. – Vidura Adikari Apr 22 '20 at 10:13
  • Worked perfectly -- took some time to figure this one out. What is considered best practice here? Most other answers involved either (1) dispatching an event to prompt a change, or (2) wrapping this in a handle function. – Pythoner Apr 21 '21 at 01:24
7

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) }}
    />
);
};
writer_chris
  • 146
  • 3
  • 9
7
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>
    )
}
Gaurav Sharma
  • 141
  • 2
  • 1
  • 2
    Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation](https://meta.stackexchange.com/q/114762/349538) would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you’ve made. – jasie Feb 02 '21 at 09:38
2

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.

Taohidul Islam
  • 5,246
  • 3
  • 26
  • 39
0

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
     });
}
StrikerVillain
  • 3,719
  • 2
  • 24
  • 41
gajuki
  • 1
-1

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.