|
| 1 | +import { graphql } from 'gatsby'; |
| 2 | +import PropTypes from 'prop-types'; |
| 3 | +import React from 'react'; |
| 4 | +import { Grid } from 'react-bootstrap'; |
| 5 | +import Layout from '../components/Layout'; |
| 6 | +import Example from '../components/Example'; |
| 7 | + |
| 8 | +const propTypes = { |
| 9 | + location: PropTypes.object.isRequired, |
| 10 | + data: PropTypes.shape({ |
| 11 | + site: PropTypes.shape({ |
| 12 | + siteMetadata: PropTypes.shape({ |
| 13 | + componentPages: PropTypes.arrayOf( |
| 14 | + PropTypes.shape({ |
| 15 | + path: PropTypes.string.isRequired, |
| 16 | + displayName: PropTypes.string.isRequired, |
| 17 | + }) |
| 18 | + ).isRequired, |
| 19 | + }).isRequired, |
| 20 | + }).isRequired, |
| 21 | + }).isRequired, |
| 22 | +}; |
| 23 | + |
| 24 | +const WithReactRouter = ({ data, location }) => ( |
| 25 | + <Layout data={data} location={location}> |
| 26 | + <Grid> |
| 27 | + <h1>Usage with React Router</h1> |
| 28 | + <p> |
| 29 | + People often want to animate route transitions, which can result in |
| 30 | + delightful UX when used in moderation. The first instict might be to use |
| 31 | + wrap all routes in <code>TransitionGroup</code>, but that approach |
| 32 | + requires hacks and falls apart easily when used with trickier components |
| 33 | + of React Router like <code>Redirect</code>. You should use{' '} |
| 34 | + <code>CSSTransition</code> for each route and manage their{' '} |
| 35 | + <code>in</code> prop on their own. |
| 36 | + </p> |
| 37 | + <p> |
| 38 | + The main challenge is the <strong>exit</strong> transition because React |
| 39 | + Router changes to a new route instantly, so we need to keep the old |
| 40 | + route around long enough to transition out of it. Fortunately,{' '} |
| 41 | + <code>Route</code>'s <code>children</code> prop also accepts a{' '} |
| 42 | + <em>function</em>, which should not be confused with the{' '} |
| 43 | + <code>render</code> prop! Unlike the <code>render</code> prop,{' '} |
| 44 | + <code>children</code> function runs whether the route is matched or not. |
| 45 | + React Router passes the object containing a <code>match</code> object, |
| 46 | + which exists if the route matches, otherwise it's <code>null</code>. |
| 47 | + This enables us to manage the <code>in</code> prop of{' '} |
| 48 | + <code>CSSTransition</code> based on the presence of <code>match</code>. |
| 49 | + </p> |
| 50 | + <p> |
| 51 | + Exit transitions will cause the content of routes to linger until they |
| 52 | + disappear, which might pose some styling challenges. Make sure that |
| 53 | + routes don't affect each other's layout, for example you can remove them |
| 54 | + from the flow of the document by using absolute or fixed positioning. |
| 55 | + </p> |
| 56 | + <blockquote> |
| 57 | + <p> |
| 58 | + <b>Note</b>: When using React Transition Group with React Router, make |
| 59 | + sure to avoid using the <code>Switch</code> component because it only |
| 60 | + executes the first matching <code>Route</code>. This would make the |
| 61 | + exit transition impossible to achieve because the exiting route will |
| 62 | + no longer match the current URL and the <code>children</code> function |
| 63 | + won't execute. |
| 64 | + </p> |
| 65 | + </blockquote> |
| 66 | + </Grid> |
| 67 | + <Example |
| 68 | + codeSandbox={{ |
| 69 | + title: 'CSSTransition + React Router', |
| 70 | + id: '38qm5m0mz1', |
| 71 | + }} |
| 72 | + /> |
| 73 | + </Layout> |
| 74 | +); |
| 75 | + |
| 76 | +WithReactRouter.propTypes = propTypes; |
| 77 | + |
| 78 | +export default WithReactRouter; |
| 79 | + |
| 80 | +export const pageQuery = graphql` |
| 81 | + query WithReactRouterQuery { |
| 82 | + site { |
| 83 | + ...Layout_site |
| 84 | + } |
| 85 | + } |
| 86 | +`; |
0 commit comments