<div id="root" />
.container {
  display: flex;
  justify-content: space-between;
}

.square {
  width: 100px;
  height: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 20px;
  color: white;
  background-color: black;
  user-select: none;
}
const Flipper = () => {
  const [ids, setIds] = React.useState(["square-1", "square-2"]);
  const rects = React.useRef(new Map()).current;

  const swap = ([a, b]) => [b, a];

  React.useEffect(() => {
    const squares = document.querySelectorAll(".square");
    
    // Cache position and size once on initial render
    for (const square of squares) {
      rects.set(square.id, square.getBoundingClientRect());
    }
  }, []);

  React.useLayoutEffect(() => {
    const squares = document.querySelectorAll(".square");

    for (const square of squares) {
      // Get previous size and position from cache
      const cachedRect = rects.get(square.id);

      if (cachedRect) {
        const nextRect = square.getBoundingClientRect();
        
        // Invert
        const translateX = cachedRect.x - nextRect.x;
        
        // Cache the next size and position
        rects.set(square.id, nextRect);
        
        // Play
        square.animate(
          [
            { transform: `translateX(${translateX}px)` },
            { transform: `translateX(0px)` }
          ],
          1000
        );
      }
    }
  }, ids);

  return (
    <div className="container">
      {ids.map((id, i) => {
        return (
          <div id={id} onClick={() => setIds(swap(ids))} className={`square`}>
            {id}
          </div>
        );
      })}
    </div>
  );
};

ReactDOM.render(<Flipper />, document.querySelector("#root"));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js