编辑记录时,组件上显示的数据属于道具。我收到错误
警告:表单propType失败:您向没有
value
处理程序的表单字段提供了onChange
道具。这将呈现一个只读字段。如果该字段应该是可变的,请使用defaultValue
。否则,请设置onChange
或readOnly
。检查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);
答案 0 :(得分:4)
受控组件意味着您提供了值和onChange
处理程序。你必须有两个,否则React会抱怨。如果您传递null
或undefined
值,也是如此,因此您希望在这些情况下默认为空字符串。例如:
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存储来分派操作,这将触发您的道具更新。