我正在创建一个应用程序,用户可以设计自己的形式。例如,指定字段的名称和应该包括的其他列的详细信息。

该组件可作为JSFiddle使用。

初始状态是这样的

var DynamicForm = React.createClass({
  getInitialState: function() {
   var items = {};
   items[1] = { name: 'field 1', populate_at: 'web_start',
                same_as: 'customer_name',
                autocomplete_from: 'customer_name', title: '' };
   items[2] = { name: 'field 2', populate_at: 'web_end',
                same_as: 'user_name', 
                    autocomplete_from: 'user_name', title: '' };

     return { items };
   },

  render: function() {
     var _this = this;
     return (
       <div>
         { Object.keys(this.state.items).map(function (key) {
           var item = _this.state.items[key];
           return (
             <div>
               <PopulateAtCheckboxes this={this}
                 checked={item.populate_at} id={key} 
                   populate_at={data.populate_at} />
            </div>
            );
        }, this)}
        <button onClick={this.newFieldEntry}>Create a new field</button>
        <button onClick={this.saveAndContinue}>Save and Continue</button>
      </div>
    );
  }

当用户改变任何值时,我想更新状态,但我很难找到正确的对象:

var PopulateAtCheckboxes = React.createClass({
  handleChange: function (e) {
     item = this.state.items[1];
     item.name = 'newName';
     items[1] = item;
     this.setState({items: items});
  },
  render: function() {
    var populateAtCheckbox = this.props.populate_at.map(function(value) {
      return (
        <label for={value}>
          <input type="radio" name={'populate_at'+this.props.id} value={value}
            onChange={this.handleChange} checked={this.props.checked == value}
            ref="populate-at"/>
          {value}
        </label>
      );
    }, this);
    return (
      <div className="populate-at-checkboxes">
        {populateAtCheckbox}
      </div>
    );
  }
});

我该怎么做呢?setState让它更新项目[1].name ?


当前回答

根据React的setState文档,使用Object。此处其他答案所建议的分配并不理想。由于setState的异步行为的性质,使用这种技术的后续调用可能会覆盖先前的调用,从而导致不希望看到的结果。

相反,React文档建议使用setState的更新器形式,它对前一个状态进行操作。请记住,当更新一个数组或对象时,你必须返回一个新的数组或对象,因为React要求我们保持状态不变性。使用ES6语法的展开操作符来浅复制数组,在数组的给定索引处创建或更新对象的属性将如下所示:

this.setState(prevState => {
    const newItems = [...prevState.items];
    newItems[index].name = newName;
    return {items: newItems};
})

其他回答

你可以使用update immutability helper:

this.setState({
  items: update(this.state.items, {1: {name: {$set: 'updated field name'}}})
})

或者,如果您不关心是否能够使用===在shouldComponentUpdate()生命周期方法中检测该项的更改,那么您可以直接编辑状态并强制组件重新呈现—这实际上与@limelights的答案相同,因为它将对象拉出状态并编辑它。

this.state.items[1].name = 'updated field name'
this.forceUpdate()

后编之外:

请参阅react-training中的简单组件通信课程,了解如何将回调函数从持有状态的父组件传递给需要触发状态更改的子组件。

发现这令人惊讶地困难,没有一个ES6传播魔法似乎工作如预期。 正在使用这样的结构来获得用于布局目的的渲染元素属性。

在这个简化的例子中,使用immutability-helper中的update方法是最直接的:

constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

改编自https://github.com/kolodny/immutability-helper#computed-property-names

要更新的数组成员是一个嵌套更复杂的对象,使用适当的深度复制方法基于复杂性。

当然有更好的方法来处理布局参数,但这是关于如何处理数组。每个子元素的相关值也可以在它们之外计算,但我发现向下传递containerState更方便,因此它们的子元素可以随意获取属性并在给定的索引处更新父状态数组。

import React from 'react'
import update from 'immutability-helper'
import { ContainerElement } from './container.component.style.js'
import ChildComponent from './child-component'
export default class ContainerComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

  updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

  // ...

  render() {
    let index = 0
    return (
      <ContainerElement>
      <ChildComponent
        index={index++}
        containerState={this.state}
        updateContainerState={this.updateContainerState}
      />
      <ChildComponent
        index={index++}
        containerState={this.state}
        updateContainerState={this.updateContainerState}
      />
      </ContainerElement>
    )
  }
}

使用数组映射与箭头函数,在一行

this.setState({
    items: this.state.items.map((item, index) =>
      index === 1 ? { ...item, name: 'newName' } : item,
   )
})

或者如果你有一个动态生成的列表,你不知道索引,但只有键或id:

let ItemsCopy = []
let x = this.state.Items.map((entry) =>{

    if(entry.id == 'theIDYoureLookingFor')
    {
        entry.PropertyToChange = 'NewProperty'
    }

    ItemsCopy.push(entry)
})


this.setState({Items:ItemsCopy});

这真的很简单。

首先从状态中取出整个items对象,按需要更新items对象的部分,并通过setState将整个items对象放回状态。

handleChange: function (e) {
  items = Object.assign(this.state.items); // Pull the entire items object out. Using object.assign is a good idea for objects.
  items[1].name = 'newName'; // update the items object as needed
  this.setState({ items }); // Put back in state
}