MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

React 条件渲染与动画效果的结合

2023-06-206.5k 阅读

React 条件渲染基础

什么是条件渲染

在 React 开发中,条件渲染是一种根据特定条件来决定是否渲染或如何渲染组件的技术。React 允许我们在 JSX 中使用 JavaScript 的条件逻辑,从而根据不同的条件呈现不同的 UI 结构。这对于创建动态且灵活的用户界面至关重要。例如,在一个用户登录系统中,根据用户是否登录,我们可能会显示不同的导航栏。如果用户已登录,导航栏可能包含“我的账户”“注销”等选项;如果用户未登录,则可能显示“登录”“注册”等选项。

使用 if 语句进行条件渲染

在 React 组件的 render 方法中,我们可以像在普通 JavaScript 中一样使用 if 语句来实现条件渲染。下面是一个简单的示例:

import React from 'react';

function UserGreeting() {
  return <p>欢迎回来!</p>;
}

function GuestGreeting() {
  return <p>请登录或注册。</p>;
}

function Greeting(props) {
  if (props.isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

export default function App() {
  const isLoggedIn = false;
  return (
    <div>
      <Greeting isLoggedIn={isLoggedIn} />
    </div>
  );
}

在上述代码中,Greeting 组件根据 props.isLoggedIn 的值来决定渲染 UserGreeting 还是 GuestGreeting 组件。如果 isLoggedIntrue,则显示欢迎回来的消息;如果为 false,则提示用户登录或注册。

使用三元运算符进行条件渲染

除了 if 语句,我们还可以使用三元运算符 condition? expressionIfTrue : expressionIfFalse 来实现更简洁的条件渲染。继续以上面的登录示例为例,代码可以改写为:

import React from 'react';

function UserGreeting() {
  return <p>欢迎回来!</p>;
}

function GuestGreeting() {
  return <p>请登录或注册。</p>;
}

function Greeting(props) {
  return props.isLoggedIn? <UserGreeting /> : <GuestGreeting />;
}

export default function App() {
  const isLoggedIn = false;
  return (
    <div>
      <Greeting isLoggedIn={isLoggedIn} />
    </div>
  );
}

这种方式在条件逻辑较为简单时,使代码更加紧凑和易读。

使用逻辑与运算符(&&)进行条件渲染

逻辑与运算符 && 也常用于 React 中的条件渲染,特别是当我们只想在某个条件为 true 时渲染一个元素,而在 false 时不渲染任何内容的情况。例如,我们有一个展示用户信息的组件,只有当用户存在时才显示用户的名字:

import React from 'react';

function UserInfo(props) {
  return (
    <div>
      {props.user && <p>用户名: {props.user.name}</p>}
    </div>
  );
}

export default function App() {
  const user = null;
  return (
    <div>
      <UserInfo user={user} />
    </div>
  );
}

在上述代码中,当 props.usernullundefined 时,&& 后面的 <p>用户名: {props.user.name}</p> 不会被渲染。只有当 props.user 存在(即 true 值)时,才会显示用户名字的段落。

React 动画效果基础

React 动画库介绍

在 React 中实现动画效果,有多种库可供选择,其中比较流行的有 react - animation - librarygsap(GreenSock Animation Platform)和 react - spring 等。

  1. react - animation - library:这是一个简单易用的动画库,它基于 CSS 动画,提供了一组预定义的动画类,通过添加和移除这些类来实现动画效果。例如,我们可以使用它提供的 fadeInfadeOut 等类来实现淡入淡出的动画。
  2. gsap:功能强大的动画库,不仅支持 CSS 动画,还能操作 SVG、Canvas 等。它具有高性能和丰富的 API,可以创建复杂的动画效果,如时间轴动画、缓动效果等。
  3. react - spring:以弹簧物理学为基础的动画库,创建的动画效果更加自然流畅,适用于创建一些具有弹性效果的动画,如卡片的弹出和收起等。

使用 CSS 动画实现基本效果

在 React 中,我们可以直接使用 CSS 动画来实现一些基本的动画效果。首先,在 CSS 文件中定义动画关键帧。例如,创建一个淡入动画:

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

然后在 React 组件中应用这个动画:

import React from'react';
import './styles.css';

function FadeInComponent() {
  return (
    <div className="fade - in - animation">
      <p>这是一个淡入的段落。</p>
    </div>
  );
}

export default FadeInComponent;

styles.css 文件中,我们将动画应用到 .fade - in - animation 类上:

.fade - in - animation {
  animation: fadeIn 1s ease - in - out;
}

这样,当组件渲染时,会有一个 1 秒的淡入动画,动画效果是从透明度 0 逐渐变为 1,并且使用 ease - in - out 的缓动函数。

使用 JavaScript 操作动画

除了纯 CSS 动画,我们还可以使用 JavaScript 来控制动画。例如,通过 React 的 useStateuseEffect 钩子来实现一个点击按钮切换动画状态的效果。假设我们要实现一个元素的展开和收起动画:

import React, { useState, useEffect } from'react';
import './styles.css';

function ExpandableComponent() {
  const [isExpanded, setIsExpanded] = useState(false);
  const handleToggle = () => {
    setIsExpanded(!isExpanded);
  };

  useEffect(() => {
    const element = document.getElementById('expandable - content');
    if (isExpanded) {
      element.style.maxHeight = element.scrollHeight + 'px';
    } else {
      element.style.maxHeight = 0;
    }
  }, [isExpanded]);

  return (
    <div>
      <button onClick={handleToggle}>{isExpanded? '收起' : '展开'}</button>
      <div id="expandable - content" className="expandable - content">
        <p>这是一些可展开和收起的内容。</p>
      </div>
    </div>
  );
}

export default ExpandableComponent;

在上述代码中,useState 用于存储元素的展开状态,useEffect 钩子在 isExpanded 状态变化时,通过操作 maxHeight 属性来实现展开和收起的动画效果。当点击按钮时,isExpanded 状态切换,从而触发动画。

React 条件渲染与动画效果的结合

基于条件渲染触发动画

在实际应用中,我们常常希望根据某个条件的变化来触发动画效果。例如,在一个待办事项列表应用中,当一个任务被标记为完成时,我们希望该任务项以动画的形式淡出列表。

首先,我们定义一个待办事项组件 TodoItem

import React, { useState } from'react';
import './styles.css';

function TodoItem(props) {
  const [isCompleted, setIsCompleted] = useState(false);
  const handleToggle = () => {
    setIsCompleted(!isCompleted);
  };

  return (
    <div className={`todo - item ${isCompleted? 'completed' : ''}`}>
      <input type="checkbox" checked={isCompleted} onChange={handleToggle} />
      <span>{props.text}</span>
    </div>
  );
}

export default TodoItem;

styles.css 文件中,我们定义完成任务项的淡出动画:

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
    display: none;
  }
}

.completed {
  animation: fadeOut 1s ease - in - out forwards;
}

isCompletedtrue 时,任务项会应用 completed 类,从而触发淡入动画,最终从页面上消失(通过 display: none)。这里的 forwards 关键字确保动画结束后保持在最后一帧的状态。

条件渲染与复杂动画的结合

在更复杂的场景中,我们可能需要结合多种动画效果和条件渲染。例如,在一个电商产品详情页面,当用户点击“添加到购物车”按钮时,产品图片会以缩放和淡入的动画效果移动到购物车图标位置,同时购物车图标会有一个跳动的动画表示有新商品加入。

首先,创建产品详情组件 ProductDetail

import React, { useState } from'react';
import './styles.css';

function ProductDetail() {
  const [isAddedToCart, setIsAddedToCart] = useState(false);
  const handleAddToCart = () => {
    setIsAddedToCart(true);
  };

  return (
    <div className="product - detail">
      <img
        src="product - image.jpg"
        alt="产品图片"
        className={`product - image ${isAddedToCart? 'added - to - cart - animation' : ''}`}
      />
      <button onClick={handleAddToCart}>添加到购物车</button>
      <div className={`cart - icon ${isAddedToCart? 'cart - icon - animation' : ''}`}>
        <i className="fas fa - shopping - cart"></i>
      </div>
    </div>
  );
}

export default ProductDetail;

styles.css 文件中定义相关动画:

@keyframes scaleAndFadeIn {
  from {
    transform: scale(0.5) translateY(0);
    opacity: 0;
  }
  to {
    transform: scale(1) translateY(-50px);
    opacity: 1;
  }
}

@keyframes cartIconBounce {
  0%, 20%, 50%, 80%, 100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-30px);
  }
  60% {
    transform: translateY(-15px);
  }
}

.added - to - cart - animation {
  animation: scaleAndFadeIn 1s ease - in - out forwards;
}

.cart - icon - animation {
  animation: cartIconBounce 0.5s ease - in - out;
}

当点击“添加到购物车”按钮后,isAddedToCart 变为 true,产品图片应用 scaleAndFadeIn 动画,实现缩放和淡入并移动到指定位置的效果,同时购物车图标应用 cartIconBounce 动画,实现跳动效果。

条件渲染与动画状态管理

在一些大型应用中,动画的状态管理变得尤为重要。例如,在一个多步骤表单应用中,每个步骤之间可能有动画过渡,并且根据用户在每个步骤的操作,动画的执行可能会有所不同。

我们可以使用 Redux 来管理动画状态。首先,安装 reduxreact - redux

npm install redux react - redux

然后创建一个 action 来触发动画状态变化,例如 actions/animationActions.js

const SET_ANIMATION_STATUS = 'SET_ANIMATION_STATUS';

export const setAnimationStatus = (status) => ({
  type: SET_ANIMATION_STATUS,
  payload: status
});

接着创建一个 reducer 来处理动画状态,例如 reducers/animationReducer.js

const initialState = {
  isAnimating: false
};

const animationReducer = (state = initialState, action) => {
  switch (action.type) {
    case SET_ANIMATION_STATUS:
      return {
      ...state,
        isAnimating: action.payload
      };
    default:
      return state;
  }
};

export default animationReducer;

store.js 文件中创建 Redux 商店:

import { createStore } from'redux';
import animationReducer from './reducers/animationReducer';

const store = createStore(animationReducer);

export default store;

在表单组件中,我们可以使用 react - reduxuseDispatchuseSelector 钩子来管理动画状态。例如,FormStep1.js

import React from'react';
import { useDispatch, useSelector } from'react - redux';
import { setAnimationStatus } from '../actions/animationActions';

function FormStep1() {
  const isAnimating = useSelector((state) => state.isAnimating);
  const dispatch = useDispatch();
  const handleNext = () => {
    // 假设这里进行一些表单验证等操作
    dispatch(setAnimationStatus(true));
  };

  return (
    <div className={`form - step - 1 ${isAnimating? 'animating' : ''}`}>
      <h2>步骤 1</h2>
      {/* 表单内容 */}
      <button onClick={handleNext}>下一步</button>
    </div>
  );
}

export default FormStep1;

styles.css 文件中定义 animating 类的动画效果:

@keyframes slideOut {
  from {
    opacity: 1;
    transform: translateX(0);
  }
  to {
    opacity: 0;
    transform: translateX(100%);
  }
}

.animating {
  animation: slideOut 1s ease - in - out forwards;
}

当点击“下一步”按钮时,isAnimating 状态改变,触发动画效果,实现表单步骤之间的动画过渡。这种方式通过 Redux 集中管理动画状态,使整个应用的动画逻辑更加清晰和易于维护。

优化条件渲染与动画结合的性能

  1. 减少不必要的渲染:在 React 中,频繁的重新渲染会影响性能。当条件渲染和动画结合时,要确保只有在真正需要时才触发渲染。例如,可以使用 React.memo 来包裹那些不依赖于父组件状态变化而变化的子组件。假设我们有一个动画展示组件 AnimatedDisplay,它只依赖于自身的内部状态:
import React from'react';

const AnimatedDisplay = React.memo((props) => {
  // 动画逻辑和渲染
  return (
    <div>
      {/* 动画元素 */}
    </div>
  );
});

export default AnimatedDisplay;

这样,当父组件重新渲染时,如果 AnimatedDisplayprops 没有变化,它不会重新渲染,从而提高性能。

  1. 合理使用 CSS 硬件加速:对于一些复杂的动画效果,如 3D 变换、opacity 变化等,可以利用 CSS 的 will - change 属性和硬件加速来提高动画性能。例如,在一个旋转动画的组件中:
@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.rotating - element {
  will - change: transform;
  animation: rotate 5s linear infinite;
  transform - style: preserve - 3d;
  /* 启用 3D 变换的硬件加速 */
  transform - origin: center center;
}

will - change: transform 提示浏览器提前准备好动画所需的资源,transform - style: preserve - 3d 启用 3D 变换的硬件加速,使动画更加流畅。

  1. 使用 requestAnimationFrame 进行动画控制:在 JavaScript 中,requestAnimationFrame 是一个高效的动画控制函数,它会在浏览器下一次重绘之前调用指定的回调函数。例如,在一个根据滚动位置触发动画的场景中:
import React, { useRef, useEffect } from'react';

function ScrollAnimation() {
  const elementRef = useRef(null);

  useEffect(() => {
    const handleScroll = () => {
      const element = elementRef.current;
      if (element) {
        const rect = element.getBoundingClientRect();
        if (rect.top < window.innerHeight && rect.bottom >= 0) {
          // 开始动画
          const animate = () => {
            // 动画逻辑,例如改变元素的透明度
            element.style.opacity = (parseFloat(element.style.opacity) || 0) + 0.1;
            if (parseFloat(element.style.opacity) < 1) {
              requestAnimationFrame(animate);
            }
          };
          requestAnimationFrame(animate);
        }
      }
    };
    window.addEventListener('scroll', handleScroll);
    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return (
    <div ref={elementRef} className="scroll - animated - element">
      <p>滚动到我时会有动画。</p>
    </div>
  );
}

export default ScrollAnimation;

这种方式可以确保动画与浏览器的刷新频率同步,避免不必要的计算和性能浪费。

通过以上对 React 条件渲染与动画效果结合的详细讲解,包括基础概念、结合方式、状态管理以及性能优化等方面,希望能帮助开发者在实际项目中创建出更加丰富、动态且高性能的用户界面。无论是简单的元素显示隐藏动画,还是复杂的多步骤交互动画,都可以通过合理运用这些技术来实现。