我正在尝试使用React交换元素的两个子元素。
<div style={{position: 'relative'}}>
{this.state.items.map((item, index) => (
<div
key={item}
style={{position: 'absolute',
transform: `translateY(${index * 20}px)`,
transition: '1s linear transform'}}>
{item}
</div>
))}
</div>
state.items
是一个包含两个项目的数组。重新排序时,两个孩子div
应该转换到新的位置。
现实中发生的事情是,当第二个元素按预期转换时,第一个元素会立即跳转。
据我所知,React认为它可以重用其中一个子元素,但不能重用另一个子元素,尽管文档说如果我们使用key
属性,它应该总是重用元素:{ {3}}(至少,这就是我理解它的方式)。
我应该在代码中更改什么才能使其按预期工作?或者它是React中的错误吗?
实例:https://facebook.github.io/react/docs/reconciliation.html
答案 0 :(得分:2)
警告:我在这个答案中做了一些假设,然而它会照亮你的一些(以及我之前的)问题。此外,我的解决方案几乎肯定会被简化,但为了回答这个问题,它应该是足够的。
这是一个很好的问题。打开开发工具并查看交换项目时实际发生的情况,我感到有些惊讶。
如果你看一下,你可以排序看看React是什么。第二个元素根本没有改变它的样式支柱,只是交换内部文本节点,而第一个元素作为新元素被放入dom中。
如果我不得不猜测,这是因为在数组中交换两个项目的方式是有效的,其中至少有一个项目被复制到临时变量并放回到数组中。
我想也许如果你使翻译随机,这两个元素都会得到新的道具和动画,但这只会让它更清晰,这不是预期的行为。
寻找解决方案:
作为一个实验,如果我们提前创建节点,并通过React.cloneElement
传递索引prop,该怎么办?当我们处于此状态时,让我们呈现span
if index === 0
和div
。无需担心的钥匙。
http://codepen.io/alex-wilmer/pen/pbaXzQ?editors=1010
开放开发工具现在可以准确地说明React的意图。 React保留元素并仅更改相关部分,在本例中为innerText节点和元素类型。因为样式完全以1:1交换,所以不需要更新样式。
<强>解决方案:强>
你可以提前生成你的React元素,将它们保存在一个数组中,因此没有任何键可以随意移动并找出如何放回到DOM中。然后使用不同的数组来跟踪预期的顺序。可能非常复杂,但它确实有效!
http://codepen.io/alex-wilmer/pen/kXZKoN?editors=1010
const Item = function (props) {
return (
<div
style={{position: 'absolute',
transform: `translateY(${props.index * 20}px)`,
transition: '0.5s linear transform'}}>
{props.children}
</div>
)
}
const App = React.createClass({
getInitialState () {
return {
items: [
{item: 'One', C: <Item>One</Item>},
{item: 'Two', C: <Item>Two</Item>}
],
order: ['One', 'Two']
};
},
swap () {
this.setState({
order: [this.state.order[1], this.state.order[0]]
});
},
render: function () {
return <div>
<button onClick={this.swap}>Swap</button>
<div style={{position: 'relative'}}>
{this.state.items.map(x =>
React.cloneElement(x.C, {
index: this.state.order.findIndex(z => z === x.item)
}))
}
</div>
</div>;
}
});