2016-07-19 61 views
5

我試圖用React交換一個元素的兩個子元素。爲什麼React的DOM協調不能按預期工作?

<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屬性,它應該總是重用元素:https://facebook.github.io/react/docs/reconciliation.html(至少,這就是我的理解)。

我應該更改我的代碼以使其按預期工作?或者它是React中的錯誤?

活生生的例子:http://codepen.io/pavelp/pen/jAkoAG

+0

反應的表現,因爲它應該是沒有保證的元素將*留在DOM *,只是它會優化什麼可以。你需要的是React的[ReactCSSTransitionGroup](https://facebook.github.io/react/docs/animation.html)。 –

+0

ReactCSSTransitionGroup用於創建進入/離開動畫。從圖書館的文檔:「[它] ...是React組件進入或離開DOM時執行CSS轉換和動畫的簡單方法」。我不確定這對重新排序動畫是否有用。 –

+0

完全正確,我以爲我之前用它來重新排序。對不起,誤導:) –

回答

2

警告:我做這個答案的一些假設,但它閃耀光你的一些(以前我的)問題。我的解決方案几乎肯定可以簡化,但爲了回答這個問題,它應該是足夠的。


這是一個很好的問題。我有些驚訝地打開開發工具,看看交換項目時發生了什麼。

如果你看看,你可以排序看看React是什麼。第二個元素根本不改變它的樣式屬性,只是交換內部文本節點,而第一個元素作爲一個新元素放入dom中。

如果我不得不猜測,這是因爲交換數組中兩個項目的方式起作用,其中至少有一個項目被複制到一個臨時變量並放回到數組中。

我認爲,也許如果你隨機翻譯,兩個元素都會得到新的樣式道具和動畫,但這只是使它更清楚,這不是預期的行爲。

在路上找到一個解決辦法

作爲一個實驗,如果我們創造了什麼節點時間提前,並通過指數支撐在通過React.cloneElement渲染。雖然我們在這裏,但如果使用index === 0div,我們將渲染一個span。沒有鑰匙擔心。

http://codepen.io/alex-wilmer/pen/pbaXzQ?editors=1010

開放的開發工具,現在說明什麼反應的打算。 React保存了元素,只改變了相關的部分,在這裏是innerText節點和元素類型。因爲樣式正好以1:1交換,所以不需要樣式更新。

解決方案:

可以生成你的反應的元素的時間提前,讓那些在一個陣列,這樣沒有鑰匙洗牌周圍,並找出如何放置回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>; 
    } 
}); 
+1

非常感謝你,亞歷克斯!實際上,我已經簡化了一些:http://codepen.io/pavelp/pen/OXQKLd - 關鍵似乎不是重新排序元素。 –

相關問題