如何做出反应以正确呈现可移动输入列表?

时间:2016-12-13 15:05:34

标签: javascript reactjs data-binding

我正在尝试在react中呈现输入列表并将输入值绑定到数组。我也试图这样做,所以列表项是可移动的。但是,当我从数组中删除一个项目时,输入项目不会更新我的期望。不是删除从数组中间删除的输入,而是删除最后一个输入并保留中间输入。

var Inputs = React.createClass({
    getInitialState: function() {
    return {
      inputarr: ['']
    };
    },
  render: function() {
    var self = this;
    return <div>{ this.state.inputarr.map(function (value, i) {
        return <div key={i}><input onChange={function (e) {self.onChangeInput(i, e)}}/>
      { i < (self.state.inputarr.length - 1) && <button onClick={function () {self.onRemove(i)}}>x</button>}
      </div>;
    })  }</div>;
  },
  onChangeInput: function (i, e) {
    this.state.inputarr[i] = e.target.value;
    if (this.state.inputarr[this.state.inputarr.length - 1] !== '') {
      this.state.inputarr.push('');
    }
    this.setState({
      inputarr: this.state.inputarr.slice(0)
    });
  },
  onRemove: function (i) {
    this.state.inputarr.splice(i, 1);
    this.setState({
      inputarr: this.state.inputarr.slice(0)
    });
  }
});

ReactDOM.render(
  <Inputs/>,
  document.getElementById('container')
);

你可以在这个小提琴中运行它:https://jsfiddle.net/vvd7hex9/1/

会发生什么?

  1. 向第一个输入添加内容,将出现第二个输入。输入3个不同的输入。
  2. 使用x按钮删除第二个输入。
  3. 删除最后一个输入。

    我期望发生什么

    要删除的中间输入,只有2个输入应包含inputarr数组中的内容。

    为什么会这样?如何修复它以删除正确的输入?

2 个答案:

答案 0 :(得分:0)

啊,这是一个经典的JavaScript问题。它与您的map声明有关。您可以详细了解具体详细信息here,但其归结为当点击事件实际触发时,i的值等于inputarr.length - 1。要解决此问题,您需要某种方法在每个循环期间保留i的值。最简单的方法是将click事件更改为:

<button onClick={self.onRemove(i)}>x</button>

并将onRemove更改为:

onRemove: function (i) {
    var self = this;
    return function(e) {
      self.state.inputarr.splice(i, 1);
      self.setState({
        inputarr: this.state.inputarr.slice(0)
      });
    }
  }

如果您不熟悉,可以找到关于闭包的更多信息here

答案 1 :(得分:0)

我认为最好有单独的Input组件和App组件。 然后,您可以创建递增和递减方法,并将它们从App传递到Input组件。我已经制作了一支小笔来展示你如何实现它 我使用了 lodash 中的一些有用的方法,所以看看它们是如何工作的。

https://codepen.io/dagman/pen/oYaYyL

代码本身。

class App extends React.Component {
    constructor(props) {
        super(props);
        this.increment = this.increment.bind(this);
        this.decrement = this.decrement.bind(this);

        this.state = { 
            quantity: [0],
        };
    }

    increment(value) {
        const { quantity } = this.state;

        this.setState({ 
            quantity: quantity.concat(_.last(quantity) + 1),
        });
    }

    decrement(el) {
        const { quantity } = this.state;
        this.setState({ quantity: _.without(quantity, el) })
    }

    render() {
        const inputs = this.state.quantity.map(x => (
            <Input 
                increment={this.increment}
                decrement={this.decrement}
                key={x}
                toDelete={x}
            />
        ));

        return (
            <form>
                {inputs}
            </form>
        );
    }

}

class Input extends React.Component {
    constructor(props) {
        super(props);
        this.onChange = this.onChange.bind(this);
        this.onBtnClick = this.onBtnClick.bind(this);

        this.state = { 
            value: '',
            shouldIncrementQuantity: true,
        };
    }

    onChange(e) {
        const value = e.target.value;
        this.setState({ value });
            if(value.trim().length > 0 && this.state.shouldIncrementQuantity) {
            this.setState({ 
                shouldIncrementQuantity: false,
            }, () => this.props.increment());
        }
    }

    onBtnClick(e) {
        e.preventDefault();
        this.props.decrement(this.props.toDelete);
    }

    render() {
        return (
            <p className="input-field">
                <input
                    type="text" 
                    value={this.state.value}
                    onChange={this.onChange}
                />
                <button onClick={this.onBtnClick}>x</button>
            </p>
        );
    }
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
);