redux-little-router is a good client side routing library for project that uses React and Redux. However, adding transition to the route pages could be tricky. redux-little-router provides a react component Fragment for use to define the route pages. Each route page component will be wrapped by a Fragment component. As far as I know (after looking at the coding), Fragment component will render the route page component it is wrapping only when the route information and conditions are matched. So it’s like a on/off switch that simply turns the route page component on or off and you won’t be able to add transition to Fragment component to transition from one page to another. Therefore, we need a custom fragment component to replace Fragment in order to introduce route transition for redux-little-router.
Goal: enable route transition for redux-little-router
Base project code: Example code from React Routing with redux-little-router, ASP.NET Core SPA will be used as starting point.
Npm packages
Add the following package to “dependencies” in file package.json
1 |
"css-transitioner": "0.1.2" |
Custom fragment component
Fortunately, building a custom fragment component (a simple one) based on router information is simple enough. It can be as simple as follow:
1 2 3 4 5 6 7 8 9 10 11 12 |
class CustomComp extends React.Component<{ show: boolean }, any> { render() { return this.props.show && this.props.children; } } export const CustomFragment = connect( (state: any, props: Props_FragTrsn) => { var show = state.router.route === props.forRoute; return { show }; } )(CustomComp); |
Simply compare the route path to the forRoute property to determine if the page is active or not. We can then enhance this custom fragment component to incorporate transition.
Adding transition using css-transitioner
We have a custom react component to perform css page transitioning. More info please check out css-transitioner, React route page transition make easy.
This component enables page transition using css. We will include sass definitions for fade in/out and slide in/out to demonstrate the transitions. Note that a built-in feature of Transitioner is the ability to maintain the scroll position of each page.
Next we enhance the custom fragment component by using Transitioner to perform transitions. Add this custom fragment component as RouteTransitioner.tsx with following codes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import * as React from 'react'; import { connect } from 'react-redux'; import { Transitioner, ITransitionStatus } from 'css-transitioner'; export { ITransitionStatus }; interface Props_FragTrsn { forRoute: string; getStatus?: (router: any, pathname: string) => ITransitionStatus; } class RouteTransitioner_Comp extends React.Component<ITransitionStatus, any> { render() { return <Transitioner {...this.props}>{this.props.children}</Transitioner>; } } const getStatus_default = (router: any, routeName: string) => { var active = router.route === routeName; var transitionStyle = 'fade-in-out'; return { active, transitionStyle }; }; export const RouteTransitioner = connect( (state: any, props: Props_FragTrsn) => { var status = (props.getStatus || getStatus_default)(state.router, props.forRoute); return status; } )(RouteTransitioner_Comp) as React.ComponentClass<Props_FragTrsn> |
RouteTransitioner is the custom fragment component used to wrap around the route page component. You can provide a custom getStatus() property for each RouteTransitioner to determine what transition style to use based on the router information. Default transition style is fade in/fade out.
Adding SASS definitions
Next we define some css styles to perform the transitions. Add the follow sass definitions as Sass > Transitions.scss:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
body { margin: 0; } .top { background-color: white; padding-top: 10px; } .trsn-anchor { position: relative; } .appeared { position: relative; left: 0; width: 100%; } .disappeared { position: absolute; opacity: 0; } .zoom-in-out { position: absolute; width: 100%; transition: all .3s ease-out; } .zoom-in-out.appearing { opacity: 1; transform: scale(1); } .zoom-in-out.disappearing, .zoom-in-out.disappeared { opacity: 0; transform: scale(0.6); } .zoom-in-out.disappeared { transition: none; } .fade-in-out { position: absolute; width: 100%; transition: opacity .3s ease-out; } .fade-in-out.appearing { opacity: 1; } .fade-in-out.disappearing, .fade-in-out.disappeared { opacity: 0; } .slide-in-right { position: absolute; left: 100%; width: 100%; transition: left .3s ease-out; } .slide-in-right.appearing { left: 0; } .slide-in-left { position: absolute; left: -100%; width: 100%; transition: left .3s ease-out; } .slide-in-left.appearing { left: 0; } .slide-out-left { position: absolute; left: 0; width: 100%; transition: left .3s ease-out; } .slide-out-left.disappearing, .slide-out-left.disappeared { left: -100%; } .slide-out-right { position: absolute; left: 0; width: 100%; transition: left .3s ease-out; } .slide-out-right.disappearing, .slide-out-right.disappeared { left: 100%; } |
Right click on Transitions.scss and click on Web Compiler > Compile file to generate the .css file. Copy Transitions.css to wwwroot/css/ folder. Then include Transition.css in the web page by adding the following lines to Views > Shared > _Layout.cshtml.
1 2 3 |
<head> <link rel="stylesheet" href="~/css/Transitions.css" type="text/css" /> </head> |
Update routes.tsx
Now we incorporate use of RouteTransitioner instead of Fragment. Import the component at the top of routes.tsx as follow:
1 |
import { RouteTransitioner, ITransitionStatus } from './RouteTransitioner'; |
Next replace App const with the following to enable transitions:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
export const App = () => ( <div> <div className='top'> <div><Link href='/'>Home</Link> <Link href='/support/About'>About</Link> <Link href='/support/Contact'>Contact</Link></div> <hr /> </div> <div className='trsn-anchor'> <RouteTransitioner forRoute='/'><HelloWorld /></RouteTransitioner> <RouteTransitioner forRoute='/support/About'>{AboutPage}</RouteTransitioner> <RouteTransitioner forRoute='/support/Contact'><div>Contact page</div></RouteTransitioner> </div> </div> ); |
That’s it. Default transition is fade in/out.
Bonus: slide in/out instead
We now provide a custom getStatus() method to use sliding instead. First remove the routes constant and then add following codes to routes.tsx:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
export const routes = { '/': { order: 1 }, '/support/About': { order: 2 }, '/support/Contact': { order: 3 } } // custom getStatus() var determineTransitionStatus = (router: any, pathname: string): ITransitionStatus => { var order = routes[pathname].order; var active = router.route === pathname; var transitionStyle = 'fade-in-out'; if (active) { // active route var order_prev = router.previous ? routes[router.previous.route].order : -1; // check order of last route if (order_prev) { if (order > order_prev) transitionStyle = 'slide-in-right'; else transitionStyle = 'slide-in-left'; } } else if (router.previous && router.previous.route === pathname) { var order_current = routes[router.route].order; if (order > order_current) transitionStyle = 'slide-out-right'; else transitionStyle = 'slide-out-left'; } return { active, transitionStyle }; } |
Then we add getStatus property to each RouteTransitioner to use the custom function:
1 |
getStatus={determineTransitionStatus} |
Transition should now become slide in/out instead.
Example code for route transition for redux-little-router is available at Github.
More Read
How to use redux-little-router: React Routing with redux-little-router, ASP.NET Core SPA