在我的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中实现这一点。
答案 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类状态。此外,始终使用克隆变量处理状态数组和对象,因为您需要一次更新整个对象/数组。不要修改指向状态对象或数组的指针变量的属性。
通过引用状态对象/数组,然后通过修改引用该对象的指针来更改其属性(意外与否),可以在代码中添加错误。您将失去有关状态更新方式的所有保证,并且将prevState
或nextState
与this.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>