Basic introduction

Results demonstrate

  • Brief introduction: I realized a list of frequently asked questions, click the list items to enter the details page, the goal is to add transition effect when the page switch, improve the user experience.
  • If there is no transition effect, the details page is very abrupt:
  • With the transition effect, it looks a little better:
  • So let’s see how that works.

React-transition-group basic knowledge

Official documentation 👉react-transition-group documentation

  • In the React project, you can use the react-transition-group animation library provided by the official website to achieve the transition effect when switching pages (route switching).

  • React-transition-group exposes three components:

    • Transition
    • CSSTransition
    • TransitionGroup
  • The most important of these is CSSTransition, and TransitionGroup is used to animate the transitions of list items. I also used these two components in my project.

  • TransitionGroup does not provide any form of animation, specific the Transition of animation depends on our package | | CSSTransition animation, so we can make different types of animation in the list.

  • The most important apis in the CSSTransition component are:

    • In: Boolean, control component show and hide, true show, false hide.

    • Timeout: number, delay, refers to the duration of the animation state. You can also pass in an object such as {exit:300, Enter :500} to set the entry and exit delays, respectively.

    • ClassNames: string, the name of the class added to the element during animation. This property is commonly used to design animations. Note that classNames are used here, not classNames.

    • UnmountOnExit: Boolean, true will remove hidden elements, false will retain the state at the end of animation without removing elements. This is usually set to true.

    • Appear: Boolean, false does not animate after CSSTransition control is loaded, true does animate immediately after CSSTransition control is loaded. Set to true if you want the component to be animated the first time it renders.

    • Key: String. This property is used with the TransitionGroup component to determine whether the animation needs to be triggered. This property is very important!

  • The classNames attribute is used to concatenate the new className with the suffix enter, exits, and done of the different animation states when the component is animated. For example, the CSSTransition component has the following attributes:

        <CSSTransition
          classNames={'fade'}
          appear={true}
          key={location.pathname}
          timeout={300}
          unmountOnExit={true} > /* omit... */ </CSSTransition>Copy the code
  • Will generatefade-enter,fade-enter-active,fade-enter-done,fade-exit,fade-exite-active,fade-exit-done,fade-appearAs well asfade-appear-activeMultiple className. Each independent className corresponds to a separate state.

The react – the router cold knowledge

  • For details about the react-router, see 👉 React-router.

  • Here we usually do not pay attention to the cold knowledge of the Switch component, but also the key to achieve the animation of the route Switch!

  • The Switch has an important property: Location. Normally we do not set the location property for this component. The difference with or without this attribute:

    • Without setting the location property:SwitchComponent subcomponents (typically Route or Redirect) match routes based on the current browser location.
    • Set the location property:SwitchThe child components of the component are matched based on the defined location.
  • After reading the basic introduction, let’s take a look at how to use react-transition-group in a project.

Complete the process

  • The react-transition-group can be used in a project before component development. The react-transition-group can be used in a project before component development. Also, since I’m using typescript in my project, I need to install @types/react-transition-group. The installation command is as follows:
yarn add react-transition-group
yarn add @types/react-transition-group --dev
Copy the code
  • To use in the entry file app.tsx:
import { createHashHistory } from 'history';
import React from 'react';
import { Router } from 'react-router';
import { Route, Switch, withRouter } from 'react-router-dom';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import routeData from './common/route'; // Route configuration import NotFound from'./views/Exception';

const history = createHashHistory();

const Routes = withRouter(({ location }) => (
  <TransitionGroup className={'router-wrapper'}>
    <CSSTransition timeout={300} classNames={'fade'} key={location.pathname} unmountOnExit={true}>
      <Switch>
        {routeData.map(({ path, component, exact }: IRouterItem) => (
          <Route key={path} path={path} component={component} exact={exact} />
        ))}
        <Route component={NotFound} />
      </Switch>
    </CSSTransition>
  </TransitionGroup>
));

const App: React.FC = () => {
  return (
    <Router history= {history}>
      <Routes />
    </Router>
  );
};

export default App;
Copy the code
  • WithRouter is used to wrap any custom components. You can pass the History, location, and match methods of the React – Router to components that are not routable.

By default, components must be routing-matched to have this.props, routing parameters, and programmatic navigation writing. However, not all components are directly connected to routing (routing to this component), so when those components need routing parameters, you can use the withRouter to pass routing parameters to the component, so you can use this.props.

  • The app.js component, for example, is usually the home page, which is opened by typing the address directly from the browser. If the withRouter is not used, the this.props of this component is null. The history, location, and match methods in props cannot be implemented.

  • To make the entry file app.tsx more concise, I have encapsulated the Routes component using the react-transition-group routing code.

  • The content of the modified entry file app.tsx is as follows:

import { createHashHistory } from 'history';
import React from 'react';
import { Router } from 'react-router';
import Routes from './components/Routes';

const history = createHashHistory();

const App: React.FC = () => {
  return (
    <Router history= {history}>
      <Routes />
    </Router>
  );
};

export default App;
Copy the code
  • RoutesThe components are as follows:
import React from 'react';
import { Route, Switch, withRouter } from 'react-router-dom';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import routeData from '.. /.. /common/route';
import NotFound from '.. /.. /views/Exception'; interface IRouterItem { component? : React.ComponentType; path? : string; exact? : boolean; } class Routes extends React.Component<any> {render () {
    const { location } = this.props;
    return (
      <TransitionGroup className={'router-wrapper'}>
        <CSSTransition
          classNames={'fade'}
          appear={true}
          key={location.pathname}
          timeout={300}
          unmountOnExit={true} > <Switch location={location}> {routeData.map(({ path, component, exact }: IRouterItem) => ( <Route key={path} path={path} component={component} exact={exact} /> ))} <Route component={NotFound} /> </Switch> </CSSTransition> </TransitionGroup> ); }}export default withRouter(Routes);
Copy the code
  • The key step to determine whether there is an animation is to complete the animation style! Since the animation is global, it should be in the global style.
  • An introduction to modular less and global LESS can be found in the previous blog 👉 React + typescript project’s customization process.
  • All we need to do is create a new index.less file in the SRC directory with the following contents:
/* Animation-related style */.fade-enter,.fade-appear {opacity: 0; } .fade-enter.fade-enter-active, .fade-appear.fade-appear-active { opacity: 1; transition: opacity 300ms ease-in; } .fade-exit { opacity: 1; } .fade-exit.fade-exit-active { opacity: 0; }Copy the code
  • TSX/index.tsx/index.tsx
import './index.less';
Copy the code
  • The above can realize the page transition effect when switching routes, and no bug (no animation for the first load, interface request twice).

⚠ ️ If you just want to achieve a transition effect, follow the above introduction to achieve

⚠ ️ If you want to understand the causes of the above two bugs, you can continue to read the following.

Practice on pit

  • It’s impossible to say no potholes, and the original code didn’t work that way. The following is to explain why there are two kinds of bugs: first loading without animation and interface request twice.

First load without animation

  • As mentioned in the previous article, I used a name calledreact-loadableThe third party library for code splitting, components on demand load. (See our previous blog at 👉 for more detailsReact + typescript project customization processGet to know)
  • However, found when usedreact-loadableThere is no transition effect when the page is switched for the first time after loading. See the following effect for details:
  • As you can see, when the refresh the page, list page (first page), and without transition effect, entering the details page is not transition effect, very abrupt, click back to the list page (animation), after entering details page and cut out the transition effect, this is the first time I met page load time switch no transition effects.
  • Because the function of this implementation is relatively simple, in order to solve this problem, we can only temporarily abandon the use of the projectreact-loadableThe components are loaded on demand at 😭.
  • The modified route configuration file route. TSX contains the following information:
// path: SRC /common/route. TSX import * as React from'react';
import DetailPage from '.. /views/DetailPage';
import Exception from '.. /views/Exception';
import HomePage from '.. /views/HomePage';

const routeConfig: any = [
  {
    path: '/',
    component: HomePage,
  },
  {
    path: '/detail/:id',
    component: DetailPage,
  },
  /**
   * Exception 页面
   */
  {
    path: '/exception/404',
    component: Exception,
  },
];

function generateRouteConfig (route: IRouteConfig[]) {
  return route.map(item => {
    return {
      key: item.path,
      exact: typeof item.exact === 'undefined' ? true: item.exact, ... item, component: item.component, }; }); }export default generateRouteConfig(routeConfig);
Copy the code
  • As for the combination of the two will appear such a bug reason is still under observation, if the boss knows can leave a message to inform, or after knowing the reason to update.

Interface request twice

  • As we all know, it’s usually in the React lifecyclecomponentDidMountMethod to call the interface (request relevant data).componentDidMountThe method is executed immediately after render() and used after pulling datasetState()Method triggers re-render.
  • At the beginning of the route switch related code encapsulationRoutesThe components are as follows:
import React from 'react';
import { Route, Switch, withRouter } from 'react-router-dom';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import routeData from '.. /.. /common/route';
import NotFound from '.. /.. /views/Exception'; interface IRouterItem { component? : React.ComponentType; path? : string; exact? : boolean; } class Routes extends React.Component<any> {render () {
    const { location } = this.props;
    return (
      <TransitionGroup className={'router-wrapper'}>
        <CSSTransition
          classNames={'fade'}
          appear={true}
          key={location.pathname}
          timeout={300}
          unmountOnExit={true} > <Switch> {routeData.map(({ path, component, exact }: IRouterItem) => ( <Route key={path} path={path} component={component} exact={exact} /> ))} <Route component={NotFound} /> </Switch> </CSSTransition> </TransitionGroup> ); }}export default withRouter(Routes);
Copy the code
  • The difference with bug-free code is that it wasn’t given beforeSwitchThe component sets the location property. After loading for the first time, when entering the detail page, it will request the interface twice.

  • Why is that? As mentioned earlier:

The Switch has an important property: Location. Normally we do not set the location property for this component. The difference with or without this attribute:

  • Without setting the location property:SwitchComponent subcomponents (typically Route or Redirect) match routes based on the current browser location.
  • Set the location property:SwitchThe child components of the component are matched based on the defined location.
  • Key code block screenshots:
  • Combine the code 👆 to see the explanation 👇 :
    • CSSTransitionThe key property in this component is the matchTransitionGroupComponent, you can use the key to determine whether the animation needs to be triggered.
    • During route switchover, the old route content disappears within a certain period of time, and the new route content is displayed. During the transition, two nodes exist at the same time. The old node displays the old route content, and the new node displays the new route content.
    • CSSTransitionThe key property in the component determines whether the node is displayed, andRouterThe location property in the component is updated when the route changes, and the pathname of location happens to be availableCSSTransitionThe key property in the component. When the route is switched, the location object is changed and the new key key causes two to appear when the page is re-renderedCSSTransition.
    • If you just giveCSSTransitionThe component configures the key property and finds that the old node matches the new route content becauseRouteBy default, the component matches against the current browser’s location, which is required in order for the old node to match against the old locationSwitchComponent’s Location property.
  • Component repeated rendering will result in repeated interface requestsSwitchAdd a location property to the component.
  • The content of this article is introduced here, welcome to leave a message, like the trouble a thumbs-up 👍, thank ❤️.