1

I'm developing a react native app and using React router native v4, and I'm trying to develop the animation part, as suggested by documentation, first I made sure that everything works without animations or transitions.

I've iterated the implementation and this is as far as I got by now:

my main component renders the following:

// app.js:render
<ConnectedRouter history={history}>
  <App />
</ConnectedRouter>

my routes.js renders the following (note the location prop passed to Switch to prevent it updating its children before the parent component):

// routes.js render
<ViewTransition pathname={location.pathname}>
  <Switch location={location}>
    <Route exact path={uri.views.main} component={Dashboard} />
    <Route path={uri.views.login} component={Login} />
    <Route path={uri.views.register} component={Register} />
  </Switch>
</ViewTransition>

and the ViewTransition that handles the animation, by now it just fadesin/out the old and the new views:

// view-transition.js
@withRouter
export default class ViewTransition extends Component {
  static propTypes = {
    children: PropTypes.node,
    location: PropTypes.object.isRequired,
  };

  state = {
    prevChildren: null,
    opacity: new Animated.Value(1)
  };

  componentWillUpdate(nextProps) {
    if (nextProps.location !== this.props.location) {
      this.setState({ prevChildren: this.props.children }, this.animateFadeIn);
    }
  }

  animateFadeIn = () => {
    Animated.timing(this.state.opacity, {
      toValue: 0,
      duration: 150
    }).start(this.animateFadeOut);
  };

  animateFadeOut = () => {
    this.setState({ prevChildren: null }, () => {
      Animated.timing(this.state.opacity, {
        toValue: 1,
        duration: 400
      }).start();
    });
  };

  render() {
    const { children } = this.props;
    const { prevChildren, opacity } = this.state;
    return (
      <Animated.View
        style={{
          ...StyleSheet.absoluteFillObject,
          opacity,
          position: "absolute"
        }}
      >
        {prevChildren || children}
      </Animated.View>
    );
  }
}

The code above is working, I can see old view fading out and new view fading in, but I have an issue, when it starts fading out, somehow the component remounts again and I can see a blink just before the animation starts, I wish to know what's wrong with my code.

Javier P
  • 1,332
  • 14
  • 21

3 Answers3

1

I could fix my code above, it happened that the method componentWillUpdate in the lifecycle of a react component, already had passed the nextProps to the children, and in the meantime my component sets the new state with old children, the Switch is preparing the render of the new ones, that produces an unmount of the oldChildren and the mount of the new children, when finally my component finishes to set the state, the old children already had been unmounted and they have to be mounted again.

The story above is the long story of "I can see a blink when my animation starts", the solution happened to be easy, I don't check stuff in componentWillUpdate anymore but in componentWillReceiveProps, since the new props will pass to the parent component before its children, it gives me enough time to catch the current children, assign them to the state, render before Switch unmounts them and keep them in the View for the fading out, so no blinking anymore.

My final view-transition.js:

// view-transition.js
export default class ViewTransition extends Component {
  static propTypes = {
    children: PropTypes.node,
    location: PropTypes.object.isRequired,
  };

  state = {
    prevChildren: null,
    opacity: new Animated.Value(1)
  };

  componentWillReceiveProps(nextProps) {
    if (nextProps.location !== this.props.location) {
      this.setState({ prevChildren: this.props.children }, this.animateFadeIn);
    }
  }

  animateFadeIn = () => {
    Animated.timing(this.state.opacity, {
      toValue: 0,
      duration: 150
    }).start(this.animateFadeOut);
  };

  animateFadeOut = () => {
    this.setState({ prevChildren: null }, () => {
      Animated.timing(this.state.opacity, {
        toValue: 1,
        duration: 400
      }).start();
    });
  };

  render() {
    const { children } = this.props;
    const { prevChildren, opacity } = this.state;
    return (
      <Animated.View
        style={{
          ...StyleSheet.absoluteFillObject,
          opacity,
          position: "absolute"
        }}
      >
        {prevChildren || children}
      </Animated.View>
    );
  }
}
Javier P
  • 1,332
  • 14
  • 21
  • 1
    I would recommend that you do not use react-router in react-native. Let navigation libraries handle the transitions. Such as react-navigation or react-native-navigation. – Noitidart Mar 02 '18 at 23:31
  • @Noitidart hey! thanks for the suggestion, I'll check it out – Javier P Mar 02 '18 at 23:36
  • 1
    My pleasure Javier. I also made the similar move and regretted it. When I first came to react-native, I was so in love with react-router v4 I wanted to use it in native too. But using these navigation l found it fits so smoothly with the platform feel, and for free, without me having to implement any animations on my own. – Noitidart Mar 03 '18 at 05:58
0

I wrote an AnimatedSwitch component to work in React Native.

There is a brief moment where the states are updating and it would flash, but I fixed that with this code:

  // Need to render the previous route for the time between not animating and animating
  if (needsAnimation && !animating) {
    const tempPreviousRoute = getPreviousRoute(
      exact,
      previousLocation,
      previousChildren,
    );

    if (tempPreviousRoute) {
      const prevRouteComp = renderPreviousRoute(tempPreviousRoute);
      return prevRouteComp;
    } else {
      return null;
    }
  }

Complete code is here.

https://javascriptrambling.blogspot.com/2020/07/react-router-native-animatedswitch.html

Kevin Williams
  • 224
  • 3
  • 5
-1

Hmm..i know it's little bit too late, maybe you can try react-router-native-animate-stack

It is subcomponent of react-router-native. But it is animatable and provide stack feature. It is inspired by switch component, if you know switch, you will know how to use this package.

Default behaviour

Customization

Oktaheta
  • 606
  • 5
  • 21
  • this doesn't need to allow the user to go back- it also doesn't save the last component. It rerenders it. – evanjmg Jan 09 '21 at 17:47