重新排序对象数组

时间:2017-05-31 21:03:30

标签: javascript reactjs

在我的React状态下,我希望通过始终重新排序3个对象的数组,将所选的一个放在中间,同时保持其他对象的升序。

现在,我在每个对象中使用订单属性来跟踪订单,但这可能不是最佳方法。

例如:

this.state = {
  selected: 'item1',
  items: [
    {
      id: 'item1',
      order: 2
    },
    {
      id: 'item2'
      order: 1
    },
    {
      id: 'item3'
      order: 3
    }
  ]
}
  

结果数组:[item2, item1 ,item3]

现在,让我们假设用户选择 item2 。我会相应地更新所选州的属性,但是如何更新属性以得到如下结果:

this.state = {
  selected: 'item2',
  items: [
    {
      id: 'item1',
      order: 1
    },
    {
      id: 'item2'
      order: 2
    },
    {
      id: 'item3'
      order: 3
    }
  ]
}
  

结果数组:[item1, item2 ,item3]

你会怎么做?我已经看到了一些可能有用的lodash实用程序函数,但我想在vanilla JavaScript中实现这一点。

4 个答案:

答案 0 :(得分:2)

我会变得懒惰,只是勾勒出你需要采取的步骤。

  • 将所选项目弹出起始数组
  • 将起始数组的第一项推入新数组
  • 将所选项目推送到新阵列
  • 将起始数组的最后一项推入新数组
  • 设置您的状态以使用新数组

答案 1 :(得分:2)

你可以做这样粗暴的事情:

// Create a local shallow copy of the state
var items = this.state.items.slice()

// Find the index of the selected item within the current items array.
var selectedItemName = this.state.selected;
function isSelectedItem(element, index, array) {
    return element.id === selectedItemName;
};
var selectedIdx = items.findIndex(isSelectedItem);

// Extract that item
var selectedItem = items[selectedIdx];

// Delete the item from the items array
items.splice(selectedIdx, 1);

// Sort the items that are left over
items.sort(function(a, b) {
    return a.id < b.id ? -1 : 1;
});

// Insert the selected item back into the array
items.splice(1, 0, selectedItem);

// Set the state to the new array
this.setState({items: items});

这假设items数组的大小始终为3!

答案 2 :(得分:0)

您可以执行以下操作:

注意:这可以假设数组中有三个项目。但是,如果还有更多,我们只需要在insert函数中指定索引位置。

 this.state = {
      selected: 'item1',
      items: [
        {
          id: 'item1',
          order: 1
        },
        {
          id: 'item2',
          order: 2
        },
        {
          id: 'item3',
          order: 3
        }
      ]
    };

    // To avoid mutation.
    const insert = (list, index, newListItem) => [
      ...list.slice(0, index), // part of array before index arg
      newListItem,
      ...list.slice(index) // part of array after index arg
    ];

  // Get selected item object.
    const selectedValue = value => this.state.items.reduce((res, val) => {
      if (val.id === selectedValue) {
        res = val;
      }

      return res;
    }, {});

    const filtered = this.state.items.filter(i => i.id !== state.selected);
    const result = insert(filtered, 1, selectedValue(this.state.selected));

我们可以摆脱额外的reduce,而不是存储id而不是选择存储项目的索引或整个对象。

当然我们需要使用this.setState({ items: result })。这个解决方案还可以确保我们不会在任何时候改变原始状态数组。

答案 3 :(得分:0)

我整理了一个可以扩展的完整工作示例,以便您可以尝试不同的方法来实现您的预​​期用例。

在这种情况下,我创建了一个按钮组件并渲染了其中的三个,以提供一种更改所选状态的方法。

要记住的重要事项,请始终使用setState()函数来更新React类状态。此外,始终使用克隆变量处理状态数组和对象,因为您需要一次更新整个对象/数组。不要修改指向状态对象或数组的指针变量的属性。

通过引用状态对象/数组,然后通过修改引用该对象的指针来更改其属性(意外与否),可以在代码中添加错误。您将失去有关状态更新方式的所有保证,并且将prevStatenextStatethis.state进行比较可能无法按预期工作。

/**
  *	@desc Sub-component that renders a button
  *	@returns {HTML} Button
  */
class ChangeStateButton extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);

    this.state = ({
      //any needed state here
    });
  }
  
  handleClick(e) {
    //calls parent method with the clicked button element and click state
    this.props.click(e.nativeEvent.toElement.id);
  }

  render() {
    
    return (
      <button 
        id = {this.props.id}
        name = {this.props.name}
        className = {this.props.className}
        onClick = {this.handleClick}  >
        Reorder to {this.props.id}!
      </button>        
    );
  }
  
}



/**
  *	@desc Creates button components to control items order in state
  *	@returns {HTML} Bound buttons
  */
class ReorderArrayExample extends React.Component {
  constructor(props) {
    super(props);
    this.reorderItems = this.reorderItems.bind(this);

    this.state = ({
      selected: 'item1',
      //added to give option of where selected will insert
      selectedIndexChoice: 1,  
      items: [
        {
          id: 'item1',
          order: 2
        },
        {
          id: 'item2',
          order: 1
        },
        {
          id: 'item3',
          order: 3
        }
      ]
    });
  }
  
  reorderItems(selected) {
  
    const {items, selectedIndexChoice} = this.state,
      selectedObjectIndex = items.findIndex(el => el.id === selected);
    
    let orderedItems = items.filter(el => el.id !== selected);
    
    //You could make a faster reorder algo. This shows a working method.
    orderedItems.sort((a,b) => { return a.order - b.order })
      .splice(selectedIndexChoice, 0, items[selectedObjectIndex]);
    
    //always update state with setState function.
    this.setState({ selected, items: orderedItems });
    
    //logging results to show that this is working
    console.log('selected: ', selected);
    console.log('Ordered Items: ', JSON.stringify(orderedItems));
  }
  
  render() {
    //buttons added to show functionality
    return (
      <div>
        <ChangeStateButton 
          id='item1' 
          name='state-button-1'
          className='state-button'
          click={this.reorderItems} />
        <ChangeStateButton 
          id='item2' 
          name='state-button-2'
          className='state-button'
          click={this.reorderItems} />
        <ChangeStateButton
          id='item3' 
          name='state-button-2'
          className='state-button'
          click={this.reorderItems} />
      </div>
    );
  }
  
}

/**
  *	@desc React Class renders full page. Would have more components in a real app.
  *	@returns {HTML} full app
  */
class App extends React.Component {
  render() {
    return (
      <div className='pg'>
        <ReorderArrayExample  />
      </div>
    );
  }
}


/**
  *	Render App to DOM
  */

/**
  *	@desc ReactDOM renders app to HTML root node
  *	@returns {DOM} full page
  */
ReactDOM.render(
  <App/>, document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root">
  <!-- This div's content will be managed by React. -->
</div>