笔记 | react-transition-group 实现路由切换过渡动画

一、react-transition-group 使用

相关技术的使用:

  • React 18
  • React router v6

React Transition Group 是一个 React 库,专门用于在 React 应用中管理和处理过渡动画效果。这个库提供了一组组件,包括 Transition、CSSTransition、SwitchTransition 和 TransitionGroup,帮助在组件的进入和退出时应用动画效果。
Transition 是一个与平台无关的组件,通常结合 CSS 完成样式。
CSSTransition 是一个常用的组件,广泛用于添加过渡动画效果。它具有动画的作用时间(timeout)和指定元素首次渲染在页面时是否进行动画(appear)等参数。
SwitchTransition 用于在两个组件显示和隐藏切换时使用。
TransitionGroup 将多个动画组件包裹在其中,一般用于列表中元素的动画。执行中有三个状态:appear,enter,exit,这需要定义对应的 CSS 样式。
React Transition Group 可以应对大量常见简单动画,但如果需要编写高级动画,建议使用其他库如 react-spring、framer-motion 等。

Layout的示例伪代码

import { CSSTransition, SwitchTransition } from 'react-transition-group';
import { useLocation, useNavigate, useOutlet } from 'react-router-dom';

const currentOutlet = useOutlet();
...
<Layout className="site-layout">
  <Content className="site-content">
    <SwitchTransition mode="out-in">
      <CSSTransition
          key={location.pathname}
          appear={true}
          timeout={300}
          classNames="page"
          unmountOnExit
      >
        {currentOutlet}
      </CSSTransition>
    </SwitchTransition>
  </Content>
</Layout>

因为 CSSTransition 的 classNames 为 page:


// 页面切换过渡动画
.page {
  position: absolute;
  left: 15px;
  right: 15px;
}

// 页面切换过渡动画 --- 进入
.page-enter {
  opacity: 0;
  transform: scale(1.1);
}

// 页面切换过渡动画 --- 进入(被激活)
.page-enter-active {
  opacity: 1;
  transform: scale(1);
  transition: opacity 300ms, transform 300ms;
}

// 页面切换过渡动画 --- 离开
.page-exit {
  opacity: 1;
  transform: scale(1);
}

// 页面切换过渡动画 --- 离开(被激活)
.page-exit-active {
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 300ms, transform 300ms;
}

注意事项:

1.网上可以搜到的许多文章里都提到,组件间的切换动画需要使用 TransitionGroup 来包裹。但是在官网最新的介绍里可以看到这段话

  • 第一反应可能是在 TransitionGroup 中包裹所有路由,但这种方法需要 hack 并且在与 React Router 的更棘手的组件(如 Redirect)一起使用时很容易崩溃。 您应该为每个路由使用 CSSTransition 并自行在prop中管理它们。”

2.使用 SwitchTransition 包裹 CSSTransition,这样不会导致页面上同时展示 A 页面和 B 页面的组件,反之使用 TransitionGroup 进行包裹,就会产生两个页面同时存在的情况。
3.最重要的一点: 不要直接使用 Outlet 及<Outlet />,而要使用useOutlet。在我没阅读官网的代码前,我尝试使用 SwitchTransition 和 CSSTransition 对 <Outlet /> 进行包裹,来实现子路由组件的展示。结果:

  • 第一个页面的离开的动画丢失了。
  • 第二个页面会被加载两次,直观反映就是页面闪烁,useEffect(()=>{},[]) 或者 componentDidMount() 被调用两次。
    因为 <Outlet /> 会导致在同一页面的新旧组件各调用一次接口,所以请使用 useOutlet。

二、 <Outlet /> 与 useOutlet 区别

<Outlet />useOutlet都是在React Router中用来实现路由导出的功能,但是它们的使用方式和场景有所不同。

  1. <Outlet />

<Outlet /> 是一个 React 组件,通常在路由配置中使用,用于指定当前路由的子组件应该被渲染到哪个位置。它是一个受控组件,也就是说,它的值(即要渲染的组件)是由父组件(通常是路由组件)控制的。当父组件的状态发生改变时,<Outlet /> 会自动更新渲染的组件。
例如,在路由配置中,你可以使用<Outlet />来指定一个位置,用于渲染当前路由的子路由组件:

<Route path="/user/:userId" component={UserPage}>
  <Outlet />
</Route>

在上面的例子中,当用户访问一个符合 /user/:userId 路径的URL时,UserPage组件会被渲染,并且子路由组件会渲染到 <Outlet /> 指定的位置。

  1. useOutlet

useOutlet 是一个自定义Hook,它允许你在函数组件中使用React Router的功能。通过使用 useOutlet,你可以在组件中获取到当前路由的子路由组件,并将其渲染到指定的位置。与 <Outlet /> 不同的是,useOutlet 是一个非受控组件,它的值是由调用 Hook 的代码控制的。useOutlet 是一个钩子函数,它用于在组件中获取当前嵌套路由的信息。如果嵌套路由没有挂载,则 useOutlet 返回 null;如果嵌套路由已经挂载,则 useOutlet 返回嵌套的路由对象。通过使用 useOutlet,可以在组件中获取嵌套路由的相关信息,例如路径、查询参数等。
下面是使用 useOutlet 的示例:

import { useOutlet } from 'react-router-dom';

function MyComponent() {
  const outlet = useOutlet();
  
  return (
    <div>
      <h1>My Component</h1>
      {outlet}
    </div>
  );
}

在上面的例子中,我们通过使用useOutlet获取到一个outlet对象,然后将这个对象渲染到组件中指定的位置。当路由发生变化时,React Router会自动更新渲染的子路由组件。
总的来说,<Outlet />useOutlet 在 React Router v6 中都用于处理嵌套路由的相关操作。<Outlet /> 更适合在 React Router 的路由配置中使用,用于在应用程序的当前路由中渲染嵌套的 Route 组件。而 useOutlet 则更适合在函数组件内部使用,用于在组件中获取当前嵌套路由的信息。

热门相关:惊艳人生   盛华   第一强者   士子风流   混在三国当军阀