score:3

Accepted answer

caveat: I'm making some assumptions in this answer, nevertheless it shines light some of your (and previously my) questions. Also my solution can almost certainly be simplified, but for the purposes of answering this question it should be adequate.


This is a great question. I was a bit surprised to open up the dev tools and see what's actually happening when swapping the items.

If you take a look, you can sort of see what React is up to. The second element is not changing its style prop at all and just swaps the inner text node, while the first element is dropped into the dom as a fresh element.

If I had to guess, this is because of the way swapping two items in a array works, where at least one item is copied to a temp variable and placed back into the array.

I thought that maybe if you make the translation random, both elements would get new style props and animate, but that only made it more clear this was not the intended behaviour.

On the way to finding a solution:

As an experiment, what if we created the nodes ahead of time, and pass the index prop in render via React.cloneElement. While we're at it, let's render a span if index === 0 and a div otherwise. No keys to worry about.

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

Opening up the dev tools now illustrates exactly what React is intending. React is preserving the elements and only changing the relevant part, in this case the innerText node and the element type. Because the styles are swapped exactly 1 : 1, no style update is needed.

Solution:

You can generate your React elements ahead of time, keep those in an array, and as such there are no keys to shuffle around and figure out how to place back into the DOM. Then use a different array to keep track of the intended order. Possibly highly convoluted, 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(x.C, { 
            index: this.state.order.findIndex(z => z === x.item) 
          }))
       }
      </div>
    </div>;
  }
});

Related Query

More Query from same tag