Why is React DOM negotiation not working as expected?

I am trying to swap two child elements of a transition element with 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 is an array of two elements. When it reorders, the two child div must move to new positions.

In fact, it happens that the second element passes as expected, the first of them instantly jumps.

As far as I can tell, React thinks that it can reuse one of the children, but not the other, although the docs say that if we use the key attribute, it should always reuse the elements: https://facebook.imtqy.com/ react / docs / reconciliation.html (at least as I understand it).

What should I change in my code for it to work properly? Or is this a bug in React?

Real-time example: http://codepen.io/pavelp/pen/jAkoAG

+5
source share
1 answer

caveat . I make some assumptions in this answer, however, it covers some of your (and earlier my) questions. Also, my decision will almost certainly be simplified, but to answer this question it must be adequate.


This is a great question. I was a little surprised to open the dev tools and see what actually happens when elements are replaced.

If you look, you can see what React is. The second element does not change its style support and simply changes the internal text of the node, and the first element falls into dom as a new element.

If I had to guess, this is due to how the exchange of two elements in the array works, where at least one element is copied to a temporary variable and put back into the array.

I thought that maybe if you make the translation random, both elements will get a new style of props and animation, but this only made it more clear, it was not the intended behavior.

On the way to finding a solution :

As an experiment, what if we created the nodes ahead of time and pass the pointer in the rendering through React.cloneElement . While we are in this, let's show span if index === 0 and a div otherwise. No keys to worry about.

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

The discovery of dev tools now illustrates what React intends. The reagent saves the elements and only changes the corresponding part, in this case, the internal text of the node and the type of the element. Since styles are swapped exactly 1: 1, updating the style is not required.

Decision:

You can pre-create React elements, save them in an array, and as such, there are no keys to shuffle and figure out how to put them back into the DOM. Then use another array to keep track of the intended order. Perhaps very confusing, but it works!

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(xC, { index: this.state.order.findIndex(z => z === x.item) })) } </div> </div>; } }); 
+2
source

All Articles