Tobias Mühl
Tobias Mühl

Reputation: 2058

CSS Transitions don't survive React Component changing inner DOM structure

I have illustrated the problem in this CodePen

const Component = ({ structure }) => {
  switch (structure) {
    case 'nested': 
      return (
        <div>
          <AnimatedComponent />
      </div>
     );
    case 'flat': 
      return 
        <AnimatedComponent />
      ;
  }
};

There's some logic in AnimatedComponent that changes the styling of the Component in an animated fashion, e.g. change the background color from black to red over a duration of 1 second. The animation is started by changing a color class on AnimatedComponent. There is CSS to handle the animation given the changed class.

When changing the DOM structure from nested to flat, the HTML element is destroyed and recreated, the transition starting state is lost (aka the browser doesn't know which class was set before because the element was newly created).

What I want React to do is to change the DOM structure with moving elements in new positions, not destroying and recreating them.

Is this possible?

I tried to use the key props on <AnimatedComponent />, but it only fixes the flash of DOM change. Animation is skipped. See Codepen. Thanks Thomas Rooney for this suggestion.

Can I tell React to apply the class changes just one tick after the position of the DOM element was changed?

Upvotes: 3

Views: 1504

Answers (1)

azium
azium

Reputation: 20614

Can I tell React to apply the class changes just one tick after the position of the DOM element was changed?

Yes, this is precisely what the setTimeout function is for. Copying your second example, where you fixed the flickering, wrapping your color action dispatch with setTimeout (with no time value, which defaults to 0), seems to fix your issue.

onColorClick: () => {
  setTimeout(() => { 
    dispatch({type: 'TOGGLE_COLOR'})
  })
},

codepen

Update: I've noticed it's a bit more reliable to add some time before the color change (second argument in setTimeout, (fn, ms). I believe this is because setState is also happening asynchronously.

onColorClick: () => {
  setTimeout(() => { 
    dispatch({type: 'TOGGLE_COLOR'})
  }, 100) <-- play around with this value
},

Upvotes: 1

Related Questions