React-spring: Interrupting CSS transition in progress causes duplication of transition target

Created on 19 Apr 2018  路  3Comments  路  Source: pmndrs/react-spring

I noticed that during a transition, if the transition is reversed due to a UI interaction before it can be completed, it causes strange DOM issues.

For example, in the demo below, if you press "toggle" to show the form but then quickly press the hide button before the transition can complete, it will duplicate the DOM element. The expected behavior should be that if the transition is interrupted or changed before it can be completed, it should not cause a duplicate DOM element as seen in the screenshot below.

chrome_2018-04-18_23-09-41

Demo: https://codesandbox.io/s/w70169q88w

bug

Most helpful comment

@szahn

Your code had a couple of problems, you are recreating the function you give to Transition on every render, thereby confusing it (and react). For transition it isn't the same component, they're different components, so it starts to wildly run mount/unmount animations. You can see react having the same problem as it complains about duplicated keys in the console.

Here's how you avoid that:

// Move out your component into a constant
// Transition can forward locals that it needs, for instance "toggle"
const Test = ({ toggle, ...styles }) => (
  <animated.div
    style={{
      ...defaultStyles,
      ...styles,
      backgroundColor: '#eee',
    }}>
    <FormComponent onToggle={toggle} />
  </animated.div>
)

class App extends React.PureComponent {
  state = { showDiv: false }
  toggle = e => this.setState(state => ({ showDiv: !state.showDiv }))
  render() {
    return (
      <div>
        <div onClick={this.toggle}>Toggle</div>
        <Transition
          config={{ tension: 280, friction: 60 }}
          from={{ opacity: 0, marginTop: '-50px' }}
          enter={{ opacity: 1, marginTop: '0px' }}
          leave={{ opacity: 0, marginTop: '-50px' }}
          toggle={this.toggle}>
          {this.state.showDiv && Test}
        </Transition>
      </div>
    )
  }
}

You also forgot to set native on Transition, so although you use animated.div in there, it has no effect. Now there's a bug in react-spring still that affects native rendering, where the component seems to spring up twice - i'm trying to find the cause for it, but just letting you know in case you want to already use your code.

All 3 comments

This happened in the router example, and only seems to affect "native" - great to have this isolated. it has something to do with the onRest which fires when dead components have animated out, possibly overriding when the same object enters the stage again. Will look into it this evening.

@szahn

Your code had a couple of problems, you are recreating the function you give to Transition on every render, thereby confusing it (and react). For transition it isn't the same component, they're different components, so it starts to wildly run mount/unmount animations. You can see react having the same problem as it complains about duplicated keys in the console.

Here's how you avoid that:

// Move out your component into a constant
// Transition can forward locals that it needs, for instance "toggle"
const Test = ({ toggle, ...styles }) => (
  <animated.div
    style={{
      ...defaultStyles,
      ...styles,
      backgroundColor: '#eee',
    }}>
    <FormComponent onToggle={toggle} />
  </animated.div>
)

class App extends React.PureComponent {
  state = { showDiv: false }
  toggle = e => this.setState(state => ({ showDiv: !state.showDiv }))
  render() {
    return (
      <div>
        <div onClick={this.toggle}>Toggle</div>
        <Transition
          config={{ tension: 280, friction: 60 }}
          from={{ opacity: 0, marginTop: '-50px' }}
          enter={{ opacity: 1, marginTop: '0px' }}
          leave={{ opacity: 0, marginTop: '-50px' }}
          toggle={this.toggle}>
          {this.state.showDiv && Test}
        </Transition>
      </div>
    )
  }
}

You also forgot to set native on Transition, so although you use animated.div in there, it has no effect. Now there's a bug in react-spring still that affects native rendering, where the component seems to spring up twice - i'm trying to find the cause for it, but just letting you know in case you want to already use your code.

@should be fixed in 4.2.0

Was this page helpful?
0 / 5 - 0 ratings

Related issues

localjo picture localjo  路  3Comments

anton-g picture anton-g  路  3Comments

sakhisheikh picture sakhisheikh  路  3Comments

cmmartin picture cmmartin  路  3Comments

cklab picture cklab  路  4Comments