如何在React中有条件地更新集合中项目的属性?

时间:2017-06-28 15:05:51

标签: javascript reactjs

我有一个以状态存储的项目集合:

this.state = {
    items: [
        { name: "foo", description: "a foo", index: 0 },
        { name: "bar", description: "a bar", index: 1 },
        { name: "herp", description: "a herp", index: 2 },
        { name: "derp", description: "a derp", index: 3 }
    ]
};

index属性表示集合中每个项目的序号位置。在某些时候,我需要重新订购这些项目。例如," derp"可能需要移到前面,因此需要更新其他项的索引:

{ name: "derp", description: "a derp", index: 0 },
{ name: "bar", description: "a bar", index: 1 },
{ name: "herp", description: "a herp", index: 2 },
{ name: "foo", description: "a foo", index: 3 }

我目前正使用update包中的immutability-helper更新状态。但是,我确信这不是正确的方法(虽然它有效):

// originalIndex is a variable holding the original index
// newIndex is a variable holding the new index

// initialise updatedItems so we can update within the loop
let updatedItems = update(this.state.items, { [originalIndex]: {'index': {$set: newIndex}}});

for (var i = newIndex; i < this.state.items.length; i++) {
    if (i !== originalIndex) {
        updatedItems = update(updatedItems, { [i]: {'index': {set$: parseInt(this.state.items[i].index) + 1}}});
    }
}

这感觉就像一场大规模的黑客攻击。

我的问题是,是否可以使用条件逻辑调用更新,因此可以用一次更新调用替换此循环吗?

2 个答案:

答案 0 :(得分:2)

假设我们从每个项目中提取index属性,您可以像这样创建新列表:

const items = this.state.items.slice();
const value = items[oldIndex];
items.splice(oldIndex, 1);         // remove the one you want to move
items.splice(newIndex, 0, value);  // add it back to the desired index

this.setState({ items });

也就是说,使用slice制作列表的(浅)副本,然后使用splice交换元素。

由于您一次只移动一个元素,因此可以使用以下方法保存一行:

const [value] = items.splice(oldIndex, 1);

这会将splice返回的数组的第一个元素指定给value

如果你想保留index(为什么?),那么你需要重新分配索引:

this.setState({ items: items.map((item, index) => ({ ...item, index })) });

答案 1 :(得分:1)

为什么不先在render()上对项目进行排序:

render(){
    let toDisplay = this.state.items.sort( (a,b) => {
        if (a.index <= b.index) {
           return -1;
        }
        if (a.index > b.index) {
           return 1;
        }
        return 0;
    });

    return(
         <div className='foo'>
            {
                toDisplay.map((item, i) => {
                    return(
                        <div className="bar" key={i}>{ item.name }</div>
                    );
                })
            }
        </div>
    );
}

然后,您只能使用以下命令更新state.items:

this.setState({
    items: yourUpdatedItems
});