Animating React Routes

Want to animate changes from one route to another? Reactium.Routing transition states give you control over the process.

Reactium uses the React Router under the hood by default, but we've made a few opinionated decisions to give you some powerful capabilities in the client application.

In Domain Model, we discuss the two methods for adding a routed component to your Reactium app. One is creating a route.js file which exports an object with properties supported by the React Router <Route> component. Likewise, your plugins can register routes dynamically of the same type using the Reactium SDKReactium.Routing.register().

These route objects have been extended in Reactium to give you control over a set of transitionary states between routes.

By default, registered routes are set to transitions: false This is intentional, because turning on transitions means you will need to write code to progress the routing state forward. This interrupts the quick / automatic loading of your routed component until such a time that your application is ready. e.g. you wish to animate the old component off the screen, and animate the new component onto the screen.

To turn on transitionary states for your routed component, set transitions to true when you define your route.

import Article from './Article';
import Product from './Product';

// This is the same as the default
const transitionStates = [
    {
        state: 'EXITING',
        active: 'previous',
    },
    {
        state: 'LOADING',
        active: 'current',
    },
    {
        state: 'ENTERING',
        active: 'current',
    },
    {
        state: 'READY',
        active: 'current',
    },
];

// route.js can export an array of routes as well
export default [
 {
   path: '/article/:id',
   component: Article,
   transitions: true,
   transitionStates,
   type: 'articles',
 },
 {
   path: '/product/:id',
   component: Product,
   transitions: true,
   transitionStates, 
   type: 'products',
 },
];

The default transition states are:

  • EXITING - exiting the previous routed component

  • LOADING - loading any data for the current routed component

  • ENTERING - the current component is entering

  • READY - the current component is fully loaded

The routed component will be passed a number of properties based on the current routing state:

Property

Description

active

"previous" or "current" indicating whether the currently rendered component is the previous exiting component, or if the current component is for the current route.

currentRoute

Object containing the routing configuration for the current matched route, including the location - router history.location object, match - the matched route object, params - route params, and search - URL search

previousRoute

If applicable, the object containing the routing configuration for the previous route, including the location - router history.location object, match - the matched route object, params - route params, and search - URL search

transitionState

The state of the routing transition, by default one of EXITING, LOADING, ENTERING, or READY, but customizable per route.

transitionStates

An array of the remaining states left to walk through, each with state and active properties.

changes

Object describing what caused the last routing state change (Boolean flags):

routeChanged - true when active route object changes

pathChanged - true when URL path has just changed

searchChanged - true when search params have just changed

notFound - true when no matching route was found for current route

transitionStateChanged - true when Reactium.Routing.nextState() was called from last routing state

Reactium.Routing will wait to progress to each transition state (including rendering your routed component), until your application callsReactium.Routing.nextState(). You can also subscribe to the routing state by registering a route listener, which will get calls whenever the routing state changes.

reactium-hooks.js
import op from 'object-path';

const routingStateHandler = async updates => {
    if (['LOADING', 'READY'].includes(op.get(updates, 'transitionState'))) {
        // unless LOADING or READY, waiting 1 second and then go to next state

        await new Promise(resolve => setTimeout(resolve, 1000));
        Reactium.Routing.nextState();
    }
};

Reactium.Routing.routeListeners.register('my-routing-observer', {
    handler: routingStateHandler,
    order: Reactium.Enums.priority.low,
});

Ok, now when clicking the links in one of our components, you'll see them EXITING, and then stop at LOADING. Let's add a hypothetical loading function to each component, and then progress the transition after loading is complete.

Article.js (version 2)
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import Reactium, { useAsyncEffect } from 'reactium-core/sdk';
import op from 'object-path';

const Article = ({
    active,
    currentRoute,
    previousRoute,
    transitionState,
    transitionStates,
    changes,
}) => {
    const [ article, setArticle ] = useState();
    
    useAsyncEffect(async mounted => {
      // this component is current and we need to load the content
      if (active === 'current' && transitionState === 'LOADING') {
        // load the article content
        const article = await Reactium.Cloud.run('content-retrieve', {
          type: { machineName: 'article' },
          objectId: currentRoute.params.id,
        });
        
        // set the component state and then progress the routing transition
        if (mounted()) {
          setArticle(article);
          Reactium.Routing.nextState();
        }
      }
    }, [active, transitionState]);

    if (transitionState === 'LOADING')
        return <div>Loading...</div>;

    return (
        <div className={transitionState.toLowerCase()}>
          <div>(A) Route Status: {transitionState}</div>
          <div>Article: {op.get(article, 'title', 'Unknown')}</div>
          <Link to={'/product/1'}>Product 1</Link>         
        </div>
    );
};

export default Article;

Now our component is responding to the LOADING transition state for the route, causing a data load to happen, and progressing the routing state forward. You could even use a tool such as Tween Max to animate your page content onto the screen, and then progress the routing forward after tweening, or you could create css transitions from the transition state class on the element.

Last updated