setState触发道具更改

时间:2014-10-24 19:49:19

标签: javascript reactjs

Examples组件从Definition组件接收数据(示例列表)。由于这些示例可以由用户编辑,因此它们以状态形式放置,状态表单还允许我们在每次状态更改时创建/删除其他字段。

输入(handleChangeExample函数)上的每次击键都会改变状态,但似乎属性(props.examples)也会更新,我无法理解为什么会这样做。

我相信在整个周期中,Examples组件属性应该保持不变/不变,除非我们显式保存块(saveBlock函数),它依次触发来自parent-Definition组件的saveExamples函数。

var Examples = React.createClass({
    getInitialState: function() {
        return {
            examples: this.props.examples
        }
    },

    editBlock: function(event) {
//        this.setState({examples: this.props.examples});
        this.setState({editingBlock: !this.state.editingBlock});
    },

    saveBlock: function(event) {
        var that = this;
        var filtered_examples = [];

        this.state.examples.forEach(function(example) {
            if (example !== '') {
                filtered_examples.push(example);
            }
        });

        this.props.saveExamples(filtered_examples);
    },

    handleChangeExample: function(i) {
        var updated_examples = this.state.examples;
        updated_examples[i] = this.refs['example_' + i].getDOMNode().value.trim();

        this.setState({examples: updated_examples});
    },

    render: function() {
        var that = this;
        var fields = {};

        console.log(this.props.examples);
        console.log(this.state.examples);

        this.state.examples.forEach(function(example, i) {
            if (example !== '') {
                fields['example-' + i] =
                    <li className='editing__entry'>
                        <input type='text'
                               key={i}
                               ref={'example_' + i}
                               onChange={that.handleChangeExample.bind(null, i)}
                               defaultValue={example} />
                    </li>;
            }
        });

        fields['example-' + (this.state.examples.length+1)] =
            <li className='editing__entry'>
                <input type='text'
                       key={this.state.examples.length+1}
                       ref={'example_' + (this.state.examples.length+1)}
                       onChange={that.handleChangeExample.bind(null, (this.state.examples.length+1))}
                       defaultValue='' />
            </li>;

        return (
            <section className='definition__examples block'>
                    <div className='editing'>
                        <ul className='editing__fields'>
                            {fields}
                        </ul>
                    </div>
            <button onClick={this.saveBlock} className='button--primary--small'>Save changes</button>

            </section>
        );
    }
});


var Definition = React.createClass({
    getInitialState: function() {
        return {
            examples: this.props.definition.examples
        }
    },

    saveExamples: function(examples) {
        this.setState({examples: examples});
    },


    render: function() {
        var editingMode = this.props.editingMode;
        var object = this;

        return (
            <li className='definition'>
            <Examples editingMode={editingMode}
                      examples={this.state.examples}
                      saveExamples={this.saveExamples} />
            </li>
        );
    }
});

module.exports = Definition;

1 个答案:

答案 0 :(得分:1)

您看到此行为是因为this.state.examples只是对this.props.examples的引用。

this.props.examples正在更新,原因与普通JavaScript中的预期行为相同:

> var a = [ 'abc', 'def', 'ghi' ];
undefined
> a
[ 'abc', 'def', 'ghi' ]
> var b = a;
undefined
> b
[ 'abc', 'def', 'ghi' ]
> b[1] = 'DEF';
'DEF'
> b
[ 'abc', 'DEF', 'ghi' ]
> a
[ 'abc', 'DEF', 'ghi' ]

有几种方法可以解决这个问题。一种是创建this.props.examples的副本并将其设置为this.state.examples(您可以通过Array.prototype.slice执行此操作):

getInitialState: function() {
    return {
        examples: this.props.examples.slice()
    }
}

我认为这将适用于您的情况,因为examples只是一个字符串数组。但是,slice只会创建数组的浅表副本,因此如果元素是对象或数组,您仍然会更新this.props.examples

另一种选择是使用Immutability Helpers来更新您的州。您可能还想查看immutable-js

以下是一个简单的示例,说明如何使用Immutability Helpers执行您要执行的操作:jsfiddle

/** @jsx React.DOM */

var SomeComponent = React.createClass( {

  getInitialState : function() {
      return {
          examples : this.props.examples,
      }
  },
  _handleChange : function( index, e) {
      var updateObj = {};
      updateObj[ index ] =  { $set : e.target.value };

      var newData = React.addons.update( this.state.examples, updateObj );

      this.setState( { examples : newData } );
  },

  render : function() {
    return (
      <div>
        <input onChange={this._handleChange.bind(this, 0)}/><br/>
        <input onChange={this._handleChange.bind(this, 1)}/><br/>
        props:
        <pre>{JSON.stringify(this.props.examples,null,2)}</pre>
        state:
        <pre>{JSON.stringify(this.state.examples,null,2)}</pre>
      </div>
    );
  }
} );

React.renderComponent(<SomeComponent examples={ [ "ABC", "DEF" ] } />, document.getElementById( 'main' ) );