首页 文章

React setState不更新简化数组

提问于
浏览
2

我有一个非常简单的自定义组件:两个带有按钮的 select 列表,用于将 optionsavailable (左)列表移动到 selected (右)列表 . 当然,移动的元素不应再显示在它移动的列表中 . 虽然两个按钮都成功地将元素添加到目标,但它不会从源中删除,因为当我将减少的项目数组传递给 setState 时,渲染仍然会返回原始列表 .

EDIT 发布了大部分组件代码以供澄清 . 问题方法是 addItemsremoveItems ,其中 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 返回类似但更短的数组是否存在一些细微差别?我可以't find anything referencing this behavior, and not sure what I'失踪 .

谢谢

2 回答

  • 0

    这是问题:

    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并且触发render时这些方法:

    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 .

  • 2

    根据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
    });
    

    Edit r40q9x3vxn

相关问题