【问题标题】:setState triggers props changessetState 触发 props 变化
【发布时间】:2014-12-20 17:21:46
【问题描述】:

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

状态在输入的每次击键时都会更改(handleChangeExample 函数),但似乎属性(props.examples)也更新了,我不知道为什么会这样。

我相信,Examples 组件的属性在整个周期内应该保持不变/不变,除非我们显式地保存块(saveBlock 函数),这又会触发父定义组件中的 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;

【问题讨论】:

    标签: javascript reactjs


    【解决方案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 只会创建 Array 的浅表副本,因此如果元素是对象或数组,您最终仍会更新 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' ) );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-24
      • 2020-09-22
      • 2021-03-27
      • 1970-01-01
      • 1970-01-01
      • 2018-10-12
      • 2021-05-17
      • 1970-01-01
      相关资源
      最近更新 更多