我有一个非常简单的自定义组件:两个select
列表,其中包含用于将options
从可用(左)列表移动到所选>的按钮strong>(右)列表。当然,移动的元素不应再显示在它移动的列表中。虽然两个按钮都成功地将元素添加到目标,但它不会从源中删除,因为当我将减少的项目数组传递给setState
时,渲染仍然会返回原始列表。
编辑发布大部分组件代码以便澄清。问题方法是addItems
和removeItems
,其中调用了setState
。在这两种情况下,减少/过滤的数组属性都是不更新的;正在添加的正常更新。
... imports
interface JoinedListState {
availableItems: ListItem[]
selectedItems: ListItem[]
}
export class JoinedList extends React.Component<JoinedListState, any>{
// Create new arrays of the proper available and selected then set the new
// state
private addItems(newItems: ListItem[]) {
let oldSelected = this.props.selectedItems;
oldSelected.push.apply(oldSelected, newItems);
let newSelected = oldSelected.sort((a, b) => {
let nameA = a.value.toUpperCase();
let nameB = b.value.toUpperCase();
if (nameA < nameB) {
return -1
}
return 1
});
let newAvailable = this.props.availableItems
.slice(0) // updated on recommendation of Sasha Kos
.filter((item) => {
return newItems.findIndex(i => i.id == item.id) == -1
});
this.setState({
availableItems: newAvailable,
selectedItems: newSelected
});
}
// Create new arrays of the proper available and selected then set the
//new state
private removeItems(removedItems: ListItem[]) {
.. same approach as addItems
let newSelected = this.props.selectedItems.filter((item) => {
// return only the items whose id does not exist on the newly
//removed items list
return removedItems.findIndex(i => i.id == item.id) == -1
})
this.setState({
availableItems: newAvailable,
selectedItems: newSelected
})
}
// Get the selected items by querying the DOM and send them to function
// to update state
addSelected(event: React.FormEvent<HTMLButtonElement>) {
// Code removed for brevity: uses the event object to find the
//selected objects and builds a ListItem array called 'selected'
//to pass to addItems
this.addItems(selected)
}
removeSelected(event: React.FormEvent<HTMLButtonElement>) {
// Code removed for brevity: uses the event object to find the
//selected objects and builds a ListItem array called 'selected'
//to pass to addItems
this.removeItems(selected)
}
render() {
let aItems = this.renderOptionList(this.props.availableItems),
sItems = this.renderOptionList(this.props.selectedItems);
return (
<div className='joined-list-container'>
<select key='available_list' className='available-list form-
control' multiple>
{aItems}
</select>
<span className='button-container'>
<button key='button1' className='btn btn-success'
onClick={this.addSelected.bind(this)}>
<span className='glyphicon glyphicon-chevron-right'>
</span>
</button>
<button key='button2' className='btn btn-danger'
onClick={this.removeSelected.bind(this)}>
<span className='glyphicon glyphicon-chevron-left'>
</span>
</button>
</span>
<select key='selected_list' className='selected-list form-
control' multiple>
{sItems}
</select>
</div>
)
}
renderOptionList(items: ListItem[]) {
return items.map((item, idx) => {
let key = `${item.value}_${idx}`
return (
<option value={item.id} key={key}>{item.value}</option>
)
})
}
}
(对不起任何有缺陷的格式,发帖很棘手)
当这启动新渲染时,selectedItems列表会使用新项正确更新,但availableItems始终是原始数组(是的,我确保newAvailable
数组已正确过滤()),甚至当我尝试
this.setState({
availableItems: [],
selectedItems: newSelected
})
我在下一个渲染中获得原始的availableItems数组。
通过setState
将类似但更短的数组返回状态是否有一些细微差别?我找不到任何引用这种行为的东西,也不确定我错过了什么。
由于
答案 0 :(得分:2)
这就是问题所在:
let oldSelected = this.props.selectedItems;
oldSelected.push.apply(oldSelected, newItems);
您正在此处更新this.props.selectedItems,但是对于availableItems:
let newAvailable = this.props.availableItems
.slice(0) // updated on recommendation of Sasha Kos
.filter((item) => {
return newItems.findIndex(i => i.id == item.id) == -1
});
在这里,您不直接更新this.props.availableItems。这很重要的原因是,当你调用setState并且渲染时会触发这些方法:
let aItems = this.renderOptionList(this.props.availableItems),
sItems = this.renderOptionList(this.props.selectedItems);
使用this.props返回数组,而不是this.state。 this.props.selectedItems已更改,因此返回不同的数组,而this.props.availableItems未更改。
tl; dr - 在将数组传递给renderOptionList方法时使用this.state而不是this.props。
答案 1 :(得分:1)
根据mozilla docs,Array.prototype.filter应该创建新的数组,但是描述的症状表明你只获得了对一个数组的2个引用,因此没有重新呈现。所以请试试这个
let newAvailable = this.props.availableItems
.slice(0) /* clones your array */
.filter((item) => {
return newItems.findIndex(i => i.id == item.id) == -1
});
this.setState({
availableItems: newAvailable,
selectedItems: newSelected
});