Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't my input value updating with React?

I have the following code in my component. It will get called when I update certain things, thereby replacing a bunch of things in the UI. Everything is updating EXCEPT the value of the input as seen by the user.

let input = {
  id: 'discount-' + geo + '-' + segment,
  value: percentage,
  disabled: applyToAll,
  placeholder: '0.00'
};

cells.push(
  <td key={'cell-' + geo + '-' + segment} className="discount-segment cfix">
    <Text {...input} />
  </td>
);

This is what <Text> returns, with things removed for clarity

return (
  <div className={containerClasses.join(' ')} id={'field-container-' + id}>
    {label}
    <input
      autoComplete="off"
      type="text"
      id={id}
      ref="input"
      {...extraProps}
      name={id}
      className={classes.join(' ')}
      defaultValue={this.props.value}
      onChange={this.props.onChange}
      />
  </div>
);

Everything renders fine. Let's say the percentage value is 5 on start, it will show 5 in the field. I then do something else that updates percentage to 50. (A console log will show the right number on re-render). However the value is only showing 5 in the UI still. I am using defaultValue on the input field, but I figure that should be changing as the whole thing re-renders from parent.

Edit Updated <Text> to set value instead of defaultValue. However then I need to use state to update the value when user types. Then when I re-render, I'm sending in new props with proper value, but of course props isn't updated. Catch-22 for me.

like image 958
Dave Stein Avatar asked Apr 18 '16 00:04

Dave Stein


1 Answers

You need to perform a couple of steps:

  1. Your input needs to only use the value and onChange props, do not use defaultValue
  2. Initialize your state using your props to set your default value
  3. Update your state when your props change

So, for example:

const MyComponent = React.createClass({

  propTypes: {
    defaultInputValue: React.PropTypes.string
  },

  getInitialState() {
    return {
      inputValue: this.props.defaultInputValue
    };
  },

  componentWillReceiveProps(nextProps) {
    if (nextProps.defaultInputValue !== this.props.inputValue) {
      //Forcibly overwrite input value to new default if the default ever changes
      this.setState({inputValue: nextProps.defaultInputValue});
    }
  },

  render() {
    return <input type="text"
                  value={this.state.inputValue}
                  onChange={e => this.setState({inputValue: e.target.value})} />;
  }
});

In general initializing state off of props is a no-no. I would probably cringe a little bit if I saw this come across in a code review as there is probably some behavior that can be simplified.

You can also do:

<input value={this.state.inputValue || this.props.defaultInputValue} />

Such that the value of the input reverts to the prop value if you ever clear out the input. In this case you wouldn't have to forcibly overwrite the state with the new props.

like image 172
dcousineau Avatar answered Oct 19 '22 06:10

dcousineau