对于在 Array.map() 中呈现的 React 组件(或有条件地),在 Framer Motion 中未触发退出过渡

Exit transition not triggered in Framer Motion for React components rendered in Array.map() (or conditionally)

我正在尝试使用 framer-motion 包的 AnimatePresence 组件来为组件设置动画,因为它们 enter/leave DOM.

我的应用程序中有一个部分使用 Array.map() 在 table 中呈现组件,其中 table 中的每个项目都有 initialanimateexittransition 道具。被映射的数组使用 React.useState 存储在状态中。组件在页面加载时正确地进入带有过渡动画的 DOM,并且它们还会在页面导航时带有过渡动画退出 DOM。

我尝试创建的效果适用于所有项目,但单击以过渡到屏幕外的项目除外。

我遇到的问题是,当使用 React.useStateArray.filter() 更新项目数组时,项目会在没有转换的情况下消失。 React.useState 使用新数组更新似乎不是问题,因为当我使用 Array.concat() (returns 一个新数组)添加重复元素时,它们会在屏幕上正确转换在 table 的底部,而现有组件保留在原处,不会重新安装。

组件不再呈现,但似乎在数组更新时实际上并未卸载它们。如果是这样,为什么不卸载它们?有没有一种方法可以使用 Array.map() 渲染组件,然后在状态更改时有条件地从父级手动卸载它们?

注意:除了 Array.map() 之外,当您使用 && 或三元运算符有条件地渲染组件时也会发生这种情况。

这是我的代码:

const data = [
  { title: "Item 1", Description: "First item", id: 1 },
  { title: "Item 2", Description: "Second item", id: 2 },
  { title: "Item 3", Description: "Third item", id: 3 },
  { title: "Item 4", Description: "Fourth item", id: 4 },
  { title: "Item 5", Description: "Fifth item", id: 5 }
];

export default function App() {
  const [arrayItems, setArrayItems] = React.useState(data);

  const handleClick = (item) => {
    // Correctly displays entrance animation for new element
    // setArrayItems(arrayItems.concat([item]));

    // Does not show exit animation for deleted elements
    setArrayItems(arrayItems.filter((element) => element.id === item.id));
  };

  React.useEffect(() => {
    setArrayItems(data);
  }, []);

  return (
    <div className="App">
      <AnimatePresence>
        <table style={{ borderCollapse: "collapse" }} key="1">
          {arrayItems.map((item) => (
            <ListItem handleClick={handleClick} item={item} key={item.id} />
          ))}
        </table>
      </AnimatePresence>
    </div>
  );
}

代码沙箱:

https://codesandbox.io/s/white-field-6evzg?file=/src/App.js

请尝试使用此解决方案

请更改以下代码,这将在删除目标项目后提供剩余项目。

setArrayItems(arrayItems.filter((element) => element.id === item.id));

改为

setArrayItems(arrayItems.filter((element) => element.id !== item.id));

再次查看 framer-motion 文档后,我弄清楚了问题所在。文档说:

“由于 React 的限制,被删除的组件必须是 AnimatePresence 的直接后代。”

所以修复只是更改我的代码:

<AnimatePresence>
  <table style={{ borderCollapse: "collapse" }}>
    {arrayItems.map((item) => (
      <ListItem
       handleClick={handleClick}
       item={item}
       key={item.id}
      />
    ))}
  </table>
</AnimatePresence>

为此:

<table style={{ borderCollapse: "collapse" }}>
  <AnimatePresence>
    {arrayItems.map((item) => (
      <ListItem
       handleClick={handleClick}
       item={item}
       key={item.id}
      />
    ))}
  </AnimatePresence>
</table>

奇怪的是,在我的实际代码中,AnimatePresence 实际上是将根 DOM 元素包装在 gatsby-browser.js 中,但是 motion 组件向下几层会成功使用退出动画进行动画处理(但仅限于页面导航)。我仍然不太确定为什么它适用于页面导航而不适用于有条件渲染的组件,但它一定与 React 在后台卸载组件的方式有关。

成帧器文档:https://www.framer.com/docs/animate-presence/##animating-custom-components