React-motion: Bug: incorrect `interpolatingStyle` value for `Motion` after `onRest`

Created on 3 Mar 2017  路  5Comments  路  Source: chenglou/react-motion

Example: http://www.webpackbin.com/N16jKp-cz

import React, {PureComponent } from 'react';
import {render} from 'react-dom';
import {Motion, spring} from 'react-motion';

class App extends PureComponent {
  constructor(...args) {
    super(...args);

    this.state = {
      x: spring(10)
    }

    this.onRest = this.onRest.bind(this);
  }

  onRest() {
   // changing to a number value
    this.setState({
      x: 100
    });
  }

  render() {

    return (
      <Motion defaultStyle={{x: 0}} style={this.state} onRest={this.onRest}>
        {interpolatingStyle => {
          return (
            <div>
              {/* on final render this should be '100' but it is the last spring value: '10' */}
              interpolated x: {interpolatingStyle.x} <br/>
              {/* on final render this renders '100' as expected */}
              current x: {JSON.stringify(this.state.x)}
            </div>
          )
        }}
      </Motion>
    )
  }
}

render(<App />, document.getElementById('app'));

Workaround: put update on the end of the stack

onRest() {
    setTimeout(() => {
      this.setState({
        x: 100
      });
    });
  };

Most helpful comment

Yeah setTimeout is necessary to use with onRest. I reckon this has something to do with async nature/batching of setState in React.

We can probably switch to setState(pureCallback) notation as now recommended. Hopefully it would eliminate the problem.

You may check https://github.com/nkbt/react-motion-loop with my workaround, which is the same...

All 5 comments

I am not sure if it is related, but I am also finding that the interpolatingStyle can pass the previous interpolatingStyle value when style is changed to a fixed value mid transition

I have ended up doing something like this to get around all issues:

 render() {
    const final = this.getFinal();

    const isNotMoving = start.x == 0 && start.y == 0 && final.x == 0 && final.y == 0;

    return (
      <Motion defaultStyle={start} style={final} onRest={this.onRest}>
        {(current) => {
           // while `start` might equal {x: 0, y: 0} and `final` might equal {x: 0, y: 0}, current can be the last computed value
          return (
             <div style={isNotMoving ? {} : getMovement(current)} >
               {this.props.children}
             </div>
          );
        }}
      </Motion>
    );
  }

Not sure how to proceed on this one :)

Yeah setTimeout is necessary to use with onRest. I reckon this has something to do with async nature/batching of setState in React.

We can probably switch to setState(pureCallback) notation as now recommended. Hopefully it would eliminate the problem.

You may check https://github.com/nkbt/react-motion-loop with my workaround, which is the same...

if you check source of react-collapse@3 you will find fairly ugly but working solution with instance vars. I had to figure out when animation ends properly and comparing with end values is not the right one, since spring can go into negative values in many cases.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

reintroducing picture reintroducing  路  4Comments

codejunkienick picture codejunkienick  路  8Comments

sdras picture sdras  路  10Comments

lzl124631x picture lzl124631x  路  9Comments

irvinebroque picture irvinebroque  路  7Comments