返回

React 中带有动画效果的数组对象移除:从渐隐到优雅消失

javascript

## 在 React 中实现带有动画效果的数组对象移除

问题

在 React 应用中,当尝试在点击芯片上的 X 时播放动画(渐隐动画)时,动画无法在退出时播放。这是因为从数组中移除元素会导致 map() 调用受到影响,动画无法应用于正在移除的元素。

解决方法

要实现带有动画效果的元素移除,我们需要创建一个过渡组件,管理过渡状态,并设置适当的 CSS 样式。此外,需要使用 setTimeout 延迟移除元素,以提供动画完成所需的时间。

步骤

  1. 创建过渡组件: 使用 React Transition Group 创建一个过渡组件,它将处理动画。

  2. 设置过渡状态: 使用状态管理来跟踪过渡状态(例如 enteringenteredexitingexited)。

  3. 应用过渡样式: 根据过渡状态设置不同的 CSS 样式,以控制元素的透明度和其他动画属性。

  4. 设置超时: 使用 setTimeout 在移除元素之前延迟一段特定时间,以提供动画完成所需的时间。

  5. 在过渡完成时移除元素: 在超时期间,从数组中移除元素。

代码示例

下面是一个代码示例,演示如何实现带有动画效果的元素移除:

import React, { useState, useRef, useEffect } from 'react';
import { Transition } from 'react-transition-group';
import { Chip } from '@mui/material';
import styled from '@mui/material/styles';

const ListItem = styled('li')(({ theme }) => ({
  margin: theme.spacing(0.5),
}));

const Student = ({ student, hDelete }) => {
  const nodeRef = useRef(null);
  const [inProp, setIn] = useState(false);

  useEffect(() => {
    setIn(true);
  }, []);

  const handleDelete = () => {
    setIn(false);
    setTimeout(() => {
      hDelete(student);
    }, 320);
  };

  const duration = 300;

  const defaultStyle = {
    transition: `opacity ${duration}ms ease-in-out`,
    opacity: 0,
  };

  const transitionStyles = {
    entering: { opacity: 1 },
    entered: { opacity: 1 },
    exiting: { opacity: 0 },
    exited: { opacity: 0 },
  };

  return (
    <Transition
      nodeRef={nodeRef}
      in={inProp}
      appear="true"
      timeout={duration}
    >
      {state => (
        <ListItem style={{ ...defaultStyle, ...transitionStyles[state] }}>
          <Chip
            variant="solid"
            size="small"
            sx={{
              height: 'auto',
              '& .MuiChip-label': {
                display: 'block',
                whiteSpace: 'normal',
              },
            }}
            label={`${student.name}`}
            onDelete={handleDelete}
          />
        </ListItem>
      )}
    </Transition>
  );
};

const CreateStudentForm = () => {
  const nameRef = useRef(null);
  const [people, setPeople] = useState([]);

  const handleKeyDown = (event, nextRef) => {
    if (event.key === 'Enter') {
      event.preventDefault();
      if (nextRef.current.id !== 'firstName') {
        nextRef.current.focus();
      } else {
        let myObj = {
          name: nameRef.current.value,
        };
        nameRef.current.value = '';
        let newPeople = JSON.parse(JSON.stringify(people));
        newPeople.push(myObj);
        setPeople(newPeople);
        nameRef.current.focus();
      }
    }
  };

  const handleDelete = (person) => {
    let newPeople = JSON.parse(JSON.stringify(people));
    let idx = people.findIndex((e) => e === person);
    newPeople.splice(idx, 1);
    setPeople(newPeople);
  };

  return (
    <div>
      <input
        id="firstName"
        ref={nameRef}
        onKeyDown={(e) => handleKeyDown(e, nameRef)}
      />
      <ul>
        {people.map((data, i) => (
          <Student key={i} student={data} hDelete={handleDelete} />
        ))}
      </ul>
    </div>
  );
};

export default CreateStudentForm;

总结

通过使用 React Transition Group 和管理状态,我们可以实现带有动画效果的元素移除。此方法允许元素在从数组中移除之前以优雅的方式消失,从而增强用户体验。

常见问题解答

  1. 为什么需要使用过渡组件?
    过渡组件负责管理动画,包括进入、离开和消失等状态。

  2. 如何设置过渡状态?
    可以使用状态管理工具,如 useState,来跟踪过渡状态(entering、entered、exiting、exited)。

  3. 如何设置超时?
    可以使用 setTimeout 来延迟移除元素,以提供动画完成所需的时间。

  4. 在过渡完成时如何移除元素?
    在超时期间,可以从数组中移除元素。

  5. 如何应用过渡样式?
    可以根据过渡状态设置不同的 CSS 样式,以控制元素的透明度和其他动画属性。