3

I have an onBlur on input text fields which auto-saves the record; The data structure is one document which contains an array of items. If i change an item's first column and then the second column quickly, the first column's save would cause an update which would reload both input fields.

How can i prevent the first save from reloading the entire row so both column values get saved?

Here's the code snippet (I mimiced a server save with a setTimeout):

class Store {
  static doc = {title: "test title", items: [{id: 1, one: "aa", two: "bbb"}, {id: 2, one: "yyy", two: "zzz"}]};

  static getDocument() {
    return this.doc;
  }

  static saveDocument(doc) {
      this.doc = doc;
  }

  static updateItemInDocument(item, callback) {
      var foundIndex;
      let updatedEntry = this.doc.items.find( (s, index) => {
        foundIndex = index;
        return s.id === item.id;
      });

      this.doc.items[foundIndex] = item;
      setTimeout(() => { console.log("updated"); callback(); }, 1000);
  }
};

const Row = React.createClass({
  getInitialState() {
    return {item: this.props.item};  
  },

  update() {
    let document = Store.getDocument();

    let updatedEntry = document.items.find( (s) => {
            return s.id === this.props.item.id;
        } );

     this.setState({ item: updatedEntry});
  },

  handleEdit() {
    this.setState({item: {
        id: this.props.item.id,
        one: this.refs.one.value,
        two: this.refs.two.value
      }
    });  
  },

  handleSave() {
    Store.updateItemInDocument(this.state.item, this.update);
  },

  render() {
    let item = this.state.item;
    console.log(item);
    return  <tr> <p>Hello</p>
        <input ref="one" type="text" onChange={this.handleEdit} onBlur={this.handleSave} value={item.one} />
        <input ref="two" type="text" onChange={this.handleEdit} onBlur={this.handleSave} value={item.two} />
      </tr>;
  }
});

const App = React.createClass({
  render() {
    let rows = Store.getDocument().items.map( (item, i) => {
      return <Row key={i} item={item} />;
    });

    return <table>
      {rows}
      </table>;
  }
});

ReactDOM.render(
  <App />,
  document.getElementById("app")
);

I also have the code as a codepen: http://codepen.io/tommychheng/pen/zBNxeW?editors=1010

tommy chheng
  • 9,108
  • 9
  • 55
  • 72

1 Answers1

3

The issue stems from the fact that tabbing out of an input field fires both the onChange and onBlur handlers.

Maybe I'm misunderstanding your use case, but here's how I'd fix this issue:

const Row = React.createClass({
  getInitialState() {
    return { item: this.props.item };
  },

  handleSave() {
    let item = {
      id: this.state.item.id,
      one: this.refs.one.value,
      two: this.refs.two.value
    }
    Store.updateItemInDocument(item); // Is it really necessary to use the callback here?
  },

  render() {
    let item = this.state.item;
    return (
      <tr>
        <p>Hello</p>
        <input ref="one" type="text" onBlur={this.handleSave} defaultValue={item.one} />
        <input ref="two" type="text" onBlur={this.handleSave} defaultValue={item.two} />
      </tr>
    );
  }
});

I switched from using the onChange handler to using a defaultValue.

Ashitaka
  • 19,028
  • 6
  • 54
  • 69
  • Thanks Ashitaka! i didn't know of defaultValue. It does solve this issue of an update overwriting the values after an update. My issue looks similar to http://stackoverflow.com/questions/37946229/how-do-i-reset-the-defaultvalue-for-a-react-input/37946963 where a commenter suggests using ``defaultValue`` results in a ``uncontrolled component``which is a bad practice. Do you have any thoughts about an ``uncontrolled component``? – tommy chheng Jun 27 '16 at 00:48
  • I don't think using `defaultValue` is a bad practice at all. It depends on your use cases. In this case, I think using `defaultValue` is perfectly valid. – Ashitaka Jun 27 '16 at 01:06