chenglou / react-motion

A spring that solves your animation problems.
MIT License
21.69k stars 1.16k forks source link

Delay willEnter on TransitionMotion? #512

Open paigecwilley opened 6 years ago

paigecwilley commented 6 years ago

Hi All,

Wondering if there is a possible solution to this. I'm using TransitionMotion to animate the loading of filtered data on a page. When you click the filter it makes the query, at the same time animating the old data away and animating in new data.

The trouble I'm having is that sometimes the animation enters before the new data appears and then the data just pops in seconds after the animation is finished. This isn't solvable with stiffness and dampening because the speed is dependent on the network connection.

Is there a way to delay willEnter until I've received the data it's supposed to transition in? I'm okay with there being a pause or some "blank space" while I wait.

I know in many examples the data is passed through with the styles using a map. Sadly I'm not able to do the same because of the way the data and styles need to be passed through.

Here is the logic, for reference:

Thanks in advance!


  constructor(props) {
    super(props);
    this.state = {
      currentFilter: null,
      cs: null,

    };
  }

changeFilter = (e) => {
  const query = (e && e.target.innerHTML) ? e.target.innerHTML.toLowerCase() : null;

  window.history.pushState({}, '', `/crafting/${query ? `${query}` : ''}`);
  this.setState(() => ({ currentFilter: query }));
}

getList = async () => {
  const { data } = await axios.get(`/api/crafting?slug=${this.state.currentFilter}`);
  this.setState({ cs: data.fields.caseStudies });
}

componentDidUpdate(prevProps, prevState) {
  if (prevState.currentFilter !== this.state.currentFilter) {
    this.getList();
  }
}

willEnter = () => ({
  scale: 0.01,
  opacity: 0.01,
})

willLeave = () => ({
  scale: spring(0.01, { stiffness: 260, damping: 25 }),
  opacity: spring(0.0, { stiffness: 260, damping: 25 }),
})

render() {
  return (
    <TransitionMotion
      styles={ [ {
        key: this.state.currentFilter,
        style: {
          scale: spring(1, { stiffness: 58, damping: 13.2 }),
          opacity: spring(1, { stiffness: 58, damping: 13.2 }),
        },
      } ] }
      willLeave={ this.willLeave }
      willEnter={ this.willEnter }
    >
      { interpolatedStyles => {

        return this.props.children(
          this.changeFilter,
          this.state.cs,
          this.state.currentFilter,
          interpolatedStyles
        );
      }
      }
    </TransitionMotion>
  );
}
}
nkbt commented 6 years ago

I don't think there is a simple solution, and it is not really ReactMotion question, but general React question.

Some thought that may help though: You can separate changeFilter and TransitionMotion into separate components Make sure TransitionMotions does not get props related to filter, but children do (connect to redux/flux store or smth) Only update/render component-wrapper (that actually uses TransitionMotion) when your data is already available

Current component does at least 3 things: 1) manages filters state in URL, 2) manages filters data, 3) manages animation. Separating all of these into 3 different components would give you more room to control how application works.

Also do not be afraid to connect more components to the store (redux/flux if you use them). Event subscriptions are cheap and allow you to effectively slice you app, simplify "parent"-components logic and reduce unnecessary re-renders (good post and talk about it: https://medium.com/@alexandereardon/performance-optimisations-for-react-applications-round-2-2042e5c9af97)