【发布时间】:2016-12-26 20:06:38
【问题描述】:
我在为依赖 API 调用的组件创建初始状态时遇到困难。
在我尝试实现它的每一种方式中,它都会进入一个循环,给我在渲染期间调用 this.setState() 的错误,这是一个反模式,...
问题:
如何根据子组件的状态为父组件实现初始状态?只有在reduxstate 中没有任何内容时才会触发
我可以显示代码,但它相当广泛,并且在多个文件之间划分。
编辑 1:根据要求 - 简化代码
父组件(过滤器)
class Filter extends Component {
constructor(props) {
super(props);
this.state = {
active: '',
period: this.props.filter.period,
periodDisplay: null,
departments: this.props.filter.departments,
departmentsDisplay: null
};
this.updateFilterDepartments = this.updateFilterDepartments.bind(this);
this.updateFilterPeriod = this.updateFilterPeriod.bind(this);
this.initDepartments = this.initDepartments.bind(this);
}
componentDidMount() {
// Getting initial period
if (!this.state.period && this.props.initialFrom && this.props.initialTo) {
this.didSelectPeriod(this.props.initialFrom, this.props.initialTo);
}
else if (this.state.period) {
let initPeriod = this.state.period.match(/.{1,2}/g);
this.didSelectPeriod(
initPeriod[2] + initPeriod[3] + '-' + initPeriod[1] + '-' + initPeriod[0],
initPeriod[6] + initPeriod[7] + '-' + initPeriod[5] + '-' + initPeriod[4]
);
}
}
componentWillUnmount(){
this.setState({active: ''});
}
updateFilterPeriod() {
this.props.updateFilterPeriod(this.state.period);
}
didSelectPeriod(rawFrom, rawTo) {
if (rawFrom && rawTo) {
let From = moment(rawFrom),
To = moment(rawTo);
if (From.diff(To, 'days') === 0) {
this.setState ({
periodDisplay: From.format('D MMM YYYY'),
period: From.format('DDMMYYYY') + To.format('DDMMYYYY')
}, () => this.updateFilterPeriod());
}
else {
this.setState ({
periodDisplay: From.format('D MMM YYYY') + ' - ' + To.format('D MMM YYYY'),
period: From.format('DDMMYYYY') + To.format('DDMMYYYY')
}, () => this.updateFilterPeriod());
}
}
else {
this.setState({
periodDisplay: null,
period: null
}, () => this.updateFilterPeriod());
}
}
updateFilterDepartments() {
this.props.updateFilterDepartments(this.state.departments);
}
didSelectDepartments(hashes) {
if (hashes.length === 0) {
this.setState({
departments: null,
departmentsDisplay: null
}, () => this.updateFilterDepartments());
}
else {
this.setState({
departments: hashes,
departmentsDisplay: hashes.length + ' department(s) selected'
}, () => this.updateFilterDepartments());
}
}
render() {
return (
<div className='FILTER_wrapper' >
<div className={classnames('FILTER_section', {active: this.state.active == 'datepicker'})} onClick={() => this.setState({active: 'datepicker'})}>
<i className="fa fa-calendar FILTER_icon FILTER_icon--datepicker"></i><div className="FILTER_input FILTER_input--datepicker">{this.state.periodDisplay || <div className="placeholder">Select a period</div>}</div>
<div className={classnames('FILTER_dropdown FILTER_dropdown--datepicker', {active: this.state.active == 'datepicker'})}>
<DatePicker className='' didSelect={(rawFrom, rawTo) => this.didSelectPeriod(rawFrom, rawTo)} />
</div>
</div>
<div className={classnames('FILTER_section', {active: this.state.active == 'departmentpicker'})} onClick={() => this.setState({active: 'departmentpicker'})}>
<i className="fa fa-tags FILTER_icon FILTER_icon--departmentpicker"></i><div className="FILTER_input FILTER_input--departmentpicker">{this.state.departments ? this.state.departmentsDisplay : <div className="placeholder">Select department(s)</div>}</div>
<div className={classnames('FILTER_dropdown FILTER_dropdown--departmentpicker', {active: this.state.active == 'departmentpicker'})}>
<DepartmentPicker didSelect={(hashes, names) => this.didSelectDepartments(hashes)} />
</div>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
departments: state.departments,
filter: state.filter
};
}
子组件(DepartmentFilter)
class DepartmentPicker extends Component {
callCallback() {
this.props.didSelect(this.state.selectedHash, this.state.selectedName);
}
handlePredefined(type) {
let newStateHash = [];
let newStateName = [];
switch(type) {
case 'all':
this.props.departments.items.map(department => {
newStateHash.push(department.hash);
newStateName.push(department.name);
});
this.setState({
selectedHash: newStateHash,
selectedName: newStateName
}, () => this.callCallback());
break;
case 'none':
this.setState({
selectedHash: newStateHash,
selectedName: newStateName
}, () => this.callCallback());
break;
}
}
handleDepartment(hash, name) {
if (this.state.selectedHash.length > 0 && this.state.selectedHash.indexOf(hash) > -1) {
let newStateHash = this.state.selectedHash;
let newStateName = this.state.selectedName;
let index = newStateHash.indexOf(hash);
newStateHash.splice(index, 1);
this.setState({
selectedHash: newStateHash,
selectedName: newStateName
}, () => this.callCallback());
}
else {
this.setState({
selectedHash: [...this.state.selectedHash, hash],
selectedName: [...this.state.selectedName, name]
}, () => this.callCallback());
}
}
renderDepartments(department) {
let inSelection = this.state.selectedHash.length > 0 && this.state.selectedHash.indexOf(department.hash) > -1 ? true : false;
return(
<li className={classnames('DEPARTMENTPICKER_department', {active: inSelection})} key={department.hash} onClick={() => this.handleDepartment(department.hash, department.name)}>
<i className={classnames('DEPARTMENTPICKER_icon', 'fa', {'fa-check': inSelection, 'fa-times': !inSelection})} style={{color: department.theme}}></i>
{department.name}
</li>
);
}
render() {
// Notice that here it waits for the end of the api call
if (this.props.departments.isFetching) {return <Loader type='spinner' />;}
return (
<div className='DEPARTMENTPICKER_wrapper'>
<ul className='DEPARTMENTPICKER_predefined'>
<li onClick={() => this.handlePredefined('all')}>All</li>
<li onClick={() => this.handlePredefined('none')}>None</li>
</ul>
<ul className="DEPARTMENTPICKER_departments">
{this.props.departments.items.map(this.renderDepartments)}
</ul>
</div>
);
}
}
function mapStateToProps(state) {
return {
departments: state.departments
};
}
export default connect(mapStateToProps)(DepartmentPicker);
编辑 2 因为有些人询问应用程序遵循的流程,所以我决定将其包含在此处:
- 父组件渲染+API调用(登录触发)
- 子组件呈现(API 调用仍在进行中)
- API 调用完成 -> 需要为子组件创建初始状态
【问题讨论】:
-
您可以使用
componentWillMount在组件渲染之前检查状态和/或进行 API 调用吗? -
不想听起来居高临下,但能够提炼出问题是一项重要的技能,尤其是在 SO 上。在大多数情况下,您也会在处理过程中得到答案。你能不能尝试显示一些代码?
-
我已经提出了一个答案,但在我意识到我没有清楚地得到你的问题后将其删除:首先:React 的渲染是一个无副作用的功能,所以没有?*等待*ing,除非你延迟整个渲染。
-
邮政编码。我们更容易看出您是否走在正确的道路上。
-
第二:你的逻辑是错误的,因为如果它依赖于子状态,它就不是初始状态。 React 从上到下渲染。你必须引入一个临时的初始状态。如果您能提出一些澄清,我会尽力提供帮助。
标签: reactjs redux react-redux