React: onChange not registering state change with paste then delaying new state changes

Created on 13 Dec 2016  路  4Comments  路  Source: facebook/react

Bug Report

onChange not registering state change with paste then delaying new state changes

Current Behavior:
I have a textinput box that has an onChange which takes a function as a prop (which sets state in a parent component). onChange is not registering an initial copy and paste. Oddly, when I type an extra key after what I pasted, it returns the string .length count of the original paste and _not_ the count with the additional character. It appears that React is one change behind, despite registering that something changed.

To make this more clear, I added some console.logs to see the behavior and found this:

  1. With the initial paste, it's triggering the onChange event (in this case, setAccountKey()) but, despite explicitly setting state, it's not setting it. The key I'm pasting is 72 characters (verified by irb), but this.state.accountKey.length is displaying as 0.
  2. When I type another character (making 73 characters), it's triggering the state change that _should have_ been initiated in step one and returning this.state.accountKey.length as 72 and this.state.accountKey as the original pasted key.
  3. When I type yet another character, it is again one step behind and now printing what it should have printed in step 2.

Here's the abridged code:

import React, { Component, PropTypes } from 'react';
import Router from 'react-router';
import AuthForm from '../components/AuthForm';
import KeyEntry from '../components/KeyEntry';

export default class AuthorizationContainer extends Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      accountKey: '',
    };
    this.setAccountKey = this.setAccountKey.bind(this);
    this.keyLengthValid = this.keyLengthValid.bind(this);
  }

  setAccountKey(event) {
    let text = event.target.value;
    this.setState({ accountKey: text });
    console.log('I got here!');
    // 'I got here' did display on initial paste
    this.keyLengthValid();
    console.log(this.state.accountKey);
    console.log(this.keyLengthValid());
    console.log(this.state.accountKey.length);

  }

  keyLengthValid() {
    return this.state.accountKey.length === 72;
  }

  render() {
    return (
      <AuthForm onSubmitKey={ this.handleSubmitKey }>
        <KeyEntry setAccountKey={ this.setAccountKey } />
      </AuthForm>
    );
  }
};

AuthorizationContainer.contextTypes = {
    router: PropTypes.object.isRequired,
};

////////////////////////

import React, { PropTypes, Component } from 'react';

export default class KeyEntry extends Component {
  constructor(props) {
    super(props);
  }
  render () {
    return (
      <textarea onChange={this.props.setAccountKey} placeholder="Please enter a valid API key." />
  );
 }
};

KeyEntry.propTypes = {
  setAccountKey: PropTypes.func.isRequired,
};

Expected behavior:
On paste, accountKey state would be the key pasted and have a length of 72.

Versions Tested:
React version: 15.4.1
Chrome version: 54.0.2840.98

Similar issue: #7211

Most helpful comment

I haven鈥檛 looked in depth yet but if you expect this.state to be updated immediately after a setState call, that is not the case.

From the documentation:

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.

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

All 4 comments

I haven鈥檛 looked in depth yet but if you expect this.state to be updated immediately after a setState call, that is not the case.

From the documentation:

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.

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

Thanks! I didn't realize that was the behavior. I'm going to try with a callback and see if I'm still having the issue.

OK! The callback worked with paste immediately! My bad on that. I wrote such a pretty bug report for nothing 馃槈

Thanks for the help @gaearon :)

Not for nothing, it helped me respond quickly!

Was this page helpful?
0 / 5 - 0 ratings