我有两个隐藏的输入字段,它们在节点获取完成且设置值时没有任何问题时,使用prop中的值进行填充,但是我看到以下警告。
Warning: A component is changing a controlled input of type hidden to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
虽然我了解受控和不受控之间的区别,但似乎无法理解为什么我的组件会在两者之间造成冲突。我的组件代码中是否有任何清楚的原因可能导致这种情况?
import React from 'react';
import isEqual from 'lodash/isEqual';
export default class DateFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
startDateValue: '',
endDateValue: ''
};
}
componentDidMount() {
this.setState({
startDateValue: this.props.startDateQuery,
endDateValue: this.props.endDateQuery
});
}
handleChange(input, value) {
this.setState({
[input]: value
})
}
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
render() {
return (
<div className="col-md-3">
<input type="hidden" name="_csrf" value={this.props.csrf} />
<div className="input-group blog-filter-date-range-picker">
<p>Blog Date Range:</p>
</div>
<div className="input-group blogFilterDatePicker">
<span className="input-group-addon"><i className="glyphicon glyphicon-calendar"></i></span>
<input type="text" name="blogDateRange" className="form-control blogFilterDatePicker" autoComplete="off" />
</div>
<input type="hidden" name="blogStartDate" className="form-control" value={this.state.startDateValue} onChange={e => this.handleChange('startDateValue', e.target.value)} />
<input type="hidden" name="blogEndDate" className="form-control" value={this.state.endDateValue} onChange={e => this.handleChange('endDateValue', e.target.value)} />
</div>
);
}
}
父项:
import React from 'react';
import CatgoryFilter from './SearchFormFilters/CategoryFilter';
import DepartmentFilter from './SearchFormFilters/DepartmentFilter';
import TeamFilter from './SearchFormFilters/TeamFilter';
import TypeFilter from './SearchFormFilters/TypeFilter';
import DateFilter from './SearchFormFilters/DateFilter';
//Activity Feed - Search Form
export default class ActivityFeedSearchForm extends React.Component {
render() {
var clearFilters;
if(this.typeQuery || this.props.categoryQuery || this.props.departmentQuery || this.props.teamQuery || this.props.startDateQuery || this.props.endDateQuery || this.props.typeQuery){
clearFilters = <a href="/app" id="clear-filter">Clear</a>;
}
return (
<div className="row">
<div className="annotation-search-form col-md-10 col-md-offset-1">
<div clas="row">
<form action="/app" method="post" className="annotation-filter-fields">
<DateFilter csrf={this.props.csrf} startDateQuery={this.props.startDateQuery} endDateQuery={this.props.endDateQuery} />
<TypeFilter typeQuery={this.props.typeQuery} />
<CatgoryFilter category={this.props.category} categoryQuery={this.props.categoryQuery} />
<DepartmentFilter department={this.props.department} departmentQuery={this.props.departmentQuery} />
<TeamFilter team={this.props.team} teamQuery={this.props.teamQuery} />
<div className="col-md-1 annotation-filter-section filter-button-container">
<button type="submit" id="annotation-filter-submit">Filter</button>
{clearFilters}
</div>
</form>
</div>
</div>
</div>
)
}
}
顶级父组件:
import React from 'react';
import fetch from 'node-fetch';
import ReactMarkdown from 'react-markdown';
import path from 'path';
import ActivityFeedSearchForm from './ActivityFeedSearchForm/ActivityFeedSearchForm';
import { API_ROOT } from '../config/api-config';
//GET /api/test and set to state
export default class ActivityFeed extends React.Component{
constructor(props, context) {
super(props, context);
this.state = this.context.data || window.__INITIAL_STATE__ || { team: [] };
}
fetchList() {
fetch(`${API_ROOT}` + '/api' + window.location.search, { compress: false })
.then(res => {
return res.json();
})
.then(data => {
this.setState({
startDateQuery: data.startDateQuery,
endDateQuery: data.endDateQuery,
});
})
.catch(err => {
console.log(err);
});
}
componentDidMount() {
this.fetchList();
}
render() {
return (
<div>
<Navigation notifications={this.state.notifications}/>
<ActivityFeedSearchForm csrf={this.state.csrf} category={this.state.category} categoryQuery={this.state.categoryQuery} department={this.state.department} departmentQuery={this.state.departmentQuery} team={this.state.team} teamQuery={this.state.teamQuery} typeQuery={this.state.typeQuery} startDateQuery={this.state.startDateQuery} endDateQuery={this.state.endDateQuery} />
<div className="activity-feed-container">
<div className="container">
<OnboardingInformation onboarding={this.state.onboardingWelcome} />
<LoadingIndicator loading={this.state.isLoading} />
<ActivityFeedLayout {...this.state} />
</div>
</div>
</div>
)
}
};
答案 0 :(得分:1)
当您只处理componentWillReceiveProps中的startDateValue和endDateValue时,最好将它们而不是整个道具进行比较。整个道具都没有进行深度平等检查,这就是为什么您会收到警告
更改
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
收件人
componentWillReceiveProps(nextProps) {
if (this.props.startDateQuery != nextProps.startDateQuery && this.props.endDateQuery != nextProps.endDateQuery){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
此外,您也不需要componentDidMount,因此请删除代码中的该部分,然后使用下面的内容更新构造函数代码
constructor(props) {
super(props);
this.state = {
startDateValue: this.props.startDateQuery ? this.props.startDateQuery: '',
endDateValue:this.props.endDateQuery ? this.props.endDateQuery: ''
};
}
}
还有
更改
<input type="hidden" name="_csrf" value={this.props.csrf} />
收件人
<input type="hidden" name="_csrf" value={this.props.csrf ? this.props.csrf : "" />
并且您没有绑定handleChange,因此可以在构造函数中手动将其绑定,或者将其更改为如下所示的箭头功能
更改
handleChange(input, value) {
this.setState({
[input]: value
})
}
收件人
handleChange = (input, value) => {
this.setState({
[input]: value
})
}