React OnAnimationEnd 问题

mls*_*712 3 javascript css reactjs

我正在 React 中制作一个简单的通知服务,但遇到了一个非常奇怪的问题,我找不到任何提及或解决方案。

我的组件有一组通知消息。每个“通知”都有一个“onClick”和“onAnimationEnd”绑定,它们调用一个函数,将它们从通知数组中删除。基本思想是通知会逐渐消失(使用 CSS 动画)然后被移除,或者允许用户手动点击通知来移除它。

有趣的错误如下。如果添加两个通知,第一个将触发其 onAnimationEnd 并移除自身。剩下的通知会突然跳到它的css动画的结尾,并且永远不会触发它自己的onAnimationEnd。

更奇怪的是,如果您添加四个通知,其中恰好两个会出现上述错误,而另外两个则正常运行。

还值得一提的是,如果您创建了两个通知,然后单击一个两个手动删除它,则剩余的通知将正常运行并触发其自己的 onAnimationEnd 功能。

因此,我不得不得出结论,循环遍历数组和 onAnimationEnd 触发器的某些组合在反应中以某种方式被窃听,除非有人可以指出此问题的解决方案。

反应代码

class Test extends React.Component {
    constructor (props) {
    super(props)
    this.state = {
      notifications: []
    }
  }

  add = () => {
    this.setState({notifications: [...this.state.notifications, 'Test']})
  }

  remove = (notification) => {
    let notificationArray = this.state.notifications
    notificationArray.splice(notificationArray.indexOf(notification), 1)
    this.setState({notifications: notificationArray})
  }

  render() {
    return (
      <div>
        <button onClick={this.add}>Add Notification</button>
        <div className="notification-container">
        {this.state.notifications.map(
          (notification,i) => {
            return (
              <div onAnimationEnd={()=>this.remove(notification)} 
                   onClick={() => this.remove(notification)}
                   className="notification" 
                   key={i}>
                   {notification}
              </div>
            )
          }
        )}
        </div>
      </div>
    );
  }
}


ReactDOM.render(
  <Test />,
  document.getElementById('root')
);
Run Code Online (Sandbox Code Playgroud)

CSS

.notification-container {
  position: fixed;
  bottom: 20px;
  right: 20px;
  width: 200px;
}

.notification {
  border: 1px solid;
  box-shadow: 0 10px 20px rgba(0,0,0,0.2), 0 6px 6px rgba(0,0,0,0.25);
  color: white;
  background: rgba(0,0,0,0.75);
  cursor: pointer;
  padding: 10px;
  margin-top: 10px;
  user-select: none;
  animation: fade 7s linear forwards;
}

@keyframes fade {
  0% {
    transform: translate(100%,0);
  }
  2% {
    transform: translate(-20px, 0);
  }
  5% {
    transform: translate(0,0);
  }
  20% {
    opacity: 1;
  }
  100% {
    opacity: 0.25;
  }
}
Run Code Online (Sandbox Code Playgroud)

工作 Codepen 链接 https://codepen.io/msorrentino/pen/xeVrwz

Dyl*_*ker 5

您正在使用数组索引作为组件键:

        {this.state.notifications.map(
          (notification,i) => {
            return (
              <div onAnimationEnd={()=>this.remove(notification)} 
                   onClick={() => this.remove(notification)}
                   className="notification" 
                   key={i}>
                   {notification}
              </div>
            )
          }
        )}
Run Code Online (Sandbox Code Playgroud)

当您这样做时,React 无法正确检测您的组件何时被移除。例如,通过删除索引 0 处的项目,并将索引 1 处的项目移动到它的位置,React 会认为具有键 0 的项目只是被修改而不是被删除。这可能会产生各种副作用,例如您所看到的。

如果您有唯一标识符,请尝试使用唯一标识符,否则使用某种递增键。

要在您的 codepen 中快速测试,请更改这些位(当然,我实际上不建议使用您的消息作为密钥):

add = () => {
    this.setState({notifications: [...this.state.notifications, Math.random().toString()]})
}

...

key={notification}
Run Code Online (Sandbox Code Playgroud)

数组索引只应作为最后的手段用作组件键。

  • 很难 100% 确定,因为我不是 100% 确定 React 如何使用键优化事物,但想象一下:(生成通知 0)-&gt;(生成通知 1)-&gt;(提前关闭通知 0)-&gt; (通知 1 继承了 0 的 `onAnimationEnd`,因为 React 认为它实际上是 0)。现在想象一下没有手动关闭的场景:(0 的 `onAnimationEnd` 触发)-&gt;(1 继承了 0 的 `onAnimationend`,但动画已经结束,所以它永远不会触发)。 (2认同)