编辑记录:道具与州

时间:2017-02-14 17:44:03

标签: javascript reactjs redux

编辑记录时,组件上显示的数据属于道具。我收到错误

  

警告:表单propType失败:您向没有value处理程序的表单字段提供了onChange道具。这将呈现一个只读字段。如果该字段应该是可变的,请使用defaultValue。否则,请设置onChangereadOnly。检查TerritoryDetail的呈现方法。

我有一种感觉,我根据文档中涉及controlled components的内容,以错误的方式实现了我的编辑记录组件。

编辑记录时,是否应该为字段值使用props?如果是这种情况,我的应用程序状态中的记录值,但如何在不使用props的情况下将我的应用程序状态同步到组件状态?

此外,道具说明编辑时选择选项应该是什么值。但组件状态用于监视select选项中的更改。当应用程序状态而不是组件状态设置道具时,组件状态如何更新记录的道具?

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getTerritory, getTerritoryMetaData, updateTerritory, modal } from '../actions/index';
import { Link } from 'react-router';
import { reduxForm } from 'redux-form';
import TerritoryTabs from './territory-tabs';

class TerritoryDetail extends Component {

  constructor(props) {
    super(props);


    this.openSearchUserQueueModal = this.openSearchUserQueueModal.bind(this);
    this.setAssignedToType = this.setAssignedToType.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
  }
  componentWillMount() {
    // console.log(this.props);
    this.props.getTerritory(this.props.params.id);
    this.props.getTerritoryMetaData();
  }

  renderTerritoryPickList(fieldName) {
    return this.props.territoryFields.map((territoryField) => {
      const shouldRender = territoryField.name === fieldName;
      if (shouldRender) {
      return territoryField.picklistValues.map((option) => {
          return<option value={option.value}>{option.label}</option>;
      });
    }
    });
  }

  setAssignedToType(event) {
    this.setState({ assignedToType : event.target.value });
  }

  openSearchUserQueueModal(searchType) {
    this.props.modal({
      type: 'SHOW_MODAL',
      modalType: 'USER_QUEUE_SEARCH',
      modalProps: {searchType}
    })
  }

  onSubmit() {
    console.log('Update button being clicked');
    this.props.updateTerritory({
        Name: this.refs[ `Name`].value,
        tpslead__Type__c: this.refs[ `tpslead__Type__c`].value,
        tpslead__Assigned_To_Type__c: this.refs[ `tpslead__Assigned_To_Type__c`].value,
        tpslead__Assigned_To__c: this.refs['tpslead__Assigned_To__c'].value,
        tpslead__Assigned_To_ID__c: this.refs['tpslead__Assigned_To_ID__c'].value
      }, this.props.params.id);
  }

  onChangeTerritoryName(event) {
    this.props.
  }

  render() {
    if(!this.props.territory) {
      return <div>Loading...</div>;
    }

    return(
      <TerritoryTabs id={this.props.params.id} listTab="detail">
        <div className="slds-form">
              <div className="slds-form-element">
            <div className="slds-form-element__label">
                  <label className="slds-align-middle" htmlFor="input1">Lead Territory Name</label>
            </div>
            <div className="slds-form-element__control">
                  <input type="text" ref="Name" className="slds-input" value={this.props.territory.Name}/>
            </div>
          </div>
              <div className="slds-form-element">
                <label className="slds-form-element__label" htmlFor="input2">Type</label>
                <div className="slds-form-element__control">
                    <div className="slds-select_container">
                        <select ref="tpslead__Type__c" className="slds-select" value={this.props.territory.tpslead__Type__c}>
                    <option></option>
                    {this.renderTerritoryPickList('tpslead__Type__c')}
                        </select>
                    </div>
                </div>
              </div>
              <div className="slds-form-element">
                <label className="slds-form-element__label" htmlFor="input3">Assigned to Type</label>
                <div className="slds-form-element__control">
                    <div className="slds-select_container">
                        <select ref="tpslead__Assigned_To_Type__c" onChange={ this.setAssignedToType } className="slds-select" value={this.props.territory.tpslead__Assigned_To_Type__c}>
                            <option></option>
                    {this.renderTerritoryPickList('tpslead__Assigned_To_Type__c')}
                        </select>
                    </div>
                </div>
              </div>
              <div className="slds-form-element">
                <label className="slds-form-element__label">Assigned To</label>
                <div className="slds-form-element__control">
              <section className="slds-clearfix">
              <input ref="tpslead__Assigned_To__c" value={this.props.territory.tpslead__Assigned_To__c} className="slds-input slds-float--left" style={{maxWidth: '95%'}} disabled/>
              <input ref="tpslead__Assigned_To_ID__c" value={this.props.territory.tpslead__Assigned_To_ID__c} type="hidden" />
              <button onClick={this.openSearchUserQueueModal.bind(this, this.props.territory.tpslead__Assigned_To_Type__c)} className="slds-button slds-button--icon-border slds-float--right" aria-live="assertive" style={{display: 'inline'}}>
                <svg className="slds-button__icon" aria-hidden="true">
                  <use xlinkHref={searchIcon}></use>
                </svg>
              </button>
              </section>
                </div>
              </div>
              <div className="slds-form-element slds-p-top--small">
            <Link to="/" className="slds-button slds-button--neutral">
              Cancel
            </Link>
                <button type="button" onClick={this.onSubmit} className="slds-button slds-button--brand">Update</button>
              </div>
        </div>
      </TerritoryTabs>
    );
  }
}

function mapStateToProps(state) {
console.log(state);

  return { territory: state.territories.single,
           territoryFields: state.territories.fields
         };

}

export default connect(mapStateToProps, { getTerritoryMetaData, getTerritory, updateTerritory, modal })(TerritoryDetail);

2 个答案:

答案 0 :(得分:4)

受控组件意味着您提供了值和onChange处理程序。你必须有两个,否则React会抱怨。如果您传递nullundefined值,也是如此,因此您希望在这些情况下默认为空字符串。例如:

export function TerritorySelect({ territory = '', options, onChange }) {
  const choices = options.map((o, i) => (
    <option key={i} value={o.value}>{o.label}</option>
  ));

  const update = e => onChange(e.target.value);

  return (
    <select value={territory} onChange={update}>
      {choices}
    </select>
  );
}

export default connect(
  state => ({ territory: state.territory.get('territory') }),
  { onChange: actions.updateTerritory }
)(TerritorySelect)

答案 1 :(得分:0)

黄金法则是,如果数据将被用户输入更改,则使用状态,否则使用道具。为了避免应用程序状态和组件状态之间的混淆,我将调用应用程序状态Redux存储。

您可以使用已有的函数mapStateToProps传递Redux商店中的任何内容,并在组件的构造函数上将其设置为状态。但是道具总是需要的,而不是你使用它的方式。要在构造函数方法上执行此操作,只需添加以下内容:

this.state = {
  // whatever you want to define goes here
  // if you want to pass props you don't need to call this, just props.foo
}

这意味着您可以毫无问题地处理您的选择状态和输入状态。

只要没有用户互动,您的道具就会在收到新道具时更新。这意味着您可以使用Redux存储来分派操作,这将触发您的道具更新。