简体   繁体   English

在 Array.map()(或有条件地)渲染的 React 组件的 Framer Motion 中未触发退出转换

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

I am trying to use the framer-motion package's AnimatePresence component to animate components as they enter/leave the DOM.我正在尝试使用framer-motion包的AnimatePresence组件在组件进入/离开 DOM 时为其设置动画。

I have a section in my app that renders components in a table using Array.map() , where each item in the table has initial , animate , exit , and transition props.我的应用程序中有一个部分使用Array.map()在表格中呈现组件,其中表格中的每个项目都有initialanimateexittransition道具。 The array which gets mapped is stored in state using React.useState .被映射的数组使用React.useState存储在 state 中。 Components correctly enter the DOM with a transition animation on page load, and they also exit the DOM with a transition on page navigation.组件在页面加载时通过转换动画正确进入 DOM,并且在页面导航时通过转换退出 DOM。

The effect I'm trying to create is for all items except the one clicked to transition off screen.我尝试创建的效果适用于所有项目,除了单击以切换屏幕的项目。

The issue I'm having is that when the array of items is updated using React.useState and Array.filter() , the items disappear without a transition.我遇到的问题是,当使用React.useStateArray.filter()更新项目数组时,项目会在没有转换的情况下消失。 It doesn't seem to be an issue with React.useState updating with a new array, because when I add duplicate elements using Array.concat() (which returns a new array), they correctly transition on screen at the bottom of the table, while the existing components remain where they are and aren't re-mounted.使用新数组更新React.useState似乎不是问题,因为当我使用Array.concat() (返回一个新数组)添加重复元素时,它们在表格底部的屏幕上正确转换,而现有组件保持原样,不会重新安装。

The components are no longer rendered, but it seems as though they are not actually being unmounted when the array updates.不再呈现组件,但似乎当数组更新时它们实际上并未被卸载。 If so, why aren't they unmounted?如果是这样,为什么不卸载它们? Is there a way to render components with Array.map() and then manually unmount them conditionally from the parent on a state change?有没有办法使用Array.map()渲染组件,然后在状态更改时有条件地从父级手动卸载它们?

Note: In addition to Array.map() this also occurs when you conditionally render a component using && or a ternary operator.注意:除了Array.map()之外,当您使用&&或三元运算符有条件地渲染组件时,也会发生这种情况。

Here's my code:这是我的代码:

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>
  );
}

code sandbox:代码沙箱:

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

After reviewing the framer-motion documentation again, I was able to figure out what the issue was.再次查看framer-motion文档后,我能够找出问题所在。 The documentation says that:文档说:

"The component being removed must be a direct descendant of AnimatePresence due to limitations with React." 由于 React 的限制,被移除的组件必须是AnimatePresence的直接后代。”

So the fix is just to change my code from this:所以修复只是改变我的代码:

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

To this:对此:

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

Strangely, in the case of my actual code, AnimatePresence is actually wrapping the root DOM element in gatsby-browser.js , but motion components several layers down would successfully animate using the exit animation (but only on page navigation).奇怪的是,就我的实际代码而言, AnimatePresence实际上是将根 DOM 元素包装在gatsby-browser.js ,但是向下几层的motion组件可以使用退出动画成功地设置动画(但仅限于页面导航)。 I'm still not quite sure why it works in the case of page navigation and not for conditionally rendered components, but it must have something to do with the way React unmounts components under the hood.我仍然不太确定为什么它适用于页面导航而不适用于条件渲染的组件,但它必须与 React 在后台卸载组件的方式有关。

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

Please try with this solution请尝试使用此解决方案

Please do the following code change, this will provide remaining item after removing the target item.请进行以下代码更改,这将在删除目标项目后提供剩余项目。

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

Changed to变成

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM