Redux/Flux(使用 ReactJS)和动画

Posted

技术标签:

【中文标题】Redux/Flux(使用 ReactJS)和动画【英文标题】:Redux/Flux (with ReactJS) and animation 【发布时间】:2017-02-22 10:04:36 【问题描述】:

我正在学习 React+Redux,但我不明白制作动画的正确方法。举个例子吧:

例如,我有一个列表,我想在点击时删除项目。如果我在那里没有动画效果,那将非常容易:点击时调度 REMOVE_ITEM 动作,reducer 从商店中删除项目并重新渲染 html

让我们添加一个点击删除订单项的动画。所以,当用户点击一个项目时,我想运行一个奇特的订单项移除效果......如何?我可以想到几种方法来做到这一点:

1) 点击时我调度REMOVE_ITEM 动作,然后reducer 在Store 中将一个项目标记为goingToBeDeleted,然后react 使用.fancy-dissolve-animation 类渲染该元素,然后我运行一个计时器来调度第二个动作@987654325 @。我不喜欢这个想法,因为仍然不清楚如何在此处添加 JS 动画(例如,使用 TweenMax)并且我运行 JS 计时器以在 CSS 动画结束时重新渲染。不好听。

2) 我以约 30 毫秒的间隔调度 ITEM_REMOVE_PROGRESS 动作,并存储一些代表动画当前状态的“值”。我也不喜欢它,因为它需要我为约 2 秒的动画复制存储约 120 次(例如,我想要平滑的 60 fps 动画),这简直是浪费内存。

3) 制作动画并仅在动画完成后发送REMOVE_ITEM。这是我能想到的最合适的方式,但我仍然希望在用户做出操作后立即在商店中进行更改。例如,动画可能需要几秒钟以上的时间,REMOVE_ITEM 可能会与后端同步——没有理由等待动画完成来进行后端 API 调用。

感谢阅读——有什么建议吗?

【问题讨论】:

没有数据影响的动画应该在redux store之外。 @MaciejSikora 是的,完全同意这里 - 这就是我的感觉。但我仍然不知道实现这一目标的好方法是什么。 :( 【参考方案1】:

React 在 ReactCSSTransitionGroup 帮助器类中为这个问题提供了一个很好的解决方案(参见https://facebook.github.io/react/docs/animation.html)。使用此选项,React 会为您处理它,通过将子节点的 DOM 状态保持在上次渲染时的状态。您只需将项目包装在 ReactCSSTransitionGroup 对象中。这会跟踪它的子元素,当它在移除子元素的情况下呈现时,它不会在没有子元素的情况下呈现,而是与子元素一起呈现,但会为子元素添加一个 CSS 类(您可以使用它来触发 CSS 动画,或为简单起见,您可以只使用 CSS 过渡)。然后,在超时后(配置为传递给 ReactCSSTransitionGroup 的道具),它将再次重新渲染,并从 DOM 中删除子节点。

要使用 ReactCSSTransitionGroup,你需要 npm install react-addons-css-transition-group,然后 require/import 'react-addons-css-transition-group'。动画文档提供了更详细的信息。

要记住的一件事 - 确保孩子们拥有独一无二的、不变的钥匙。仅使用索引作为键会使其行为不正确。

【讨论】:

太棒了,这更接近我正在寻找的东西。很快就会尝试,非常感谢! 谢谢你,正是我需要的!抱歉回复晚了:P【参考方案2】:

即时动作在保存状态的redux中是有问题的,所以如果我们发送动作并且它会改变存储,那么存储中的这种变化在下一个状态中是可用的,所以我们可能会出现动画反复显示的情况,因为在存储中这样参数已设置。

我对 redux 即时操作的解决方案是添加一些 id,例如(操作示例代码):


    type:"SOME_ANIMATION",
    id: new Date().getTime() //we get timestamp of animation init

运行动画的组件中的下一个保存最后一个动画ID,如果它的匹配不做动画。我使用组件状态,例如(组件代码):

componentDidUpdate ()

if (this.lastAnimationId===this.props.animation.id)
return; //the same animation id so do not do anything

//here setState or do animation because it is new one

this.lastAnimationId=this.props.animation.id; //here set new id of last abnimation


谢谢id,我们只能有一个动作,而没有反转状态的动作。超时后反转动作可能会导致问题,因为如果在反转动作之前发送其他动作(与组件连接),则动画可以重新开始。

我提出的方法的缺点是动画数据存在于状态中,但也存在动画 id,它为我们提供了有关它的信息。所以我们可以说 store 保存了上次发送的动画。

【讨论】:

感谢您的解决方案。谁负责调度ANIMATION_FINISHED 动作?动画本身? 在这个解决方案中您不需要这样的操作。我在关于反向操作的文本中对其进行了描述。它是带有 id 的即时操作,因此它保持在状态。 哦,我明白你的意思了。不幸的是,我仍然无法清楚地说明这对删除操作有何帮助。通过使用您的解决方案,我必须将项目标记为“已删除”+添加它们的动画 ID。虽然这可以工作,但仍然不是很好,因为我将删除商店中的物品:( 如果您想在动作上运行动画,那么没有理由将其从商店中删除。如果像我展示的动画 id 就是你所需要的。动作被发送到存储,动画运行,如果状态改变但我们有相同的动画 id,那么这意味着它是旧的。 ... 使已删除的组件再次重​​新渲染,即使它是不可见的。我明白你在说什么,但我希望这里有一个更清洁的解决方案。谢谢 - 我会等几天等待任何其他想法,如果没有任何好的结果 - 我会接受你的回答

以上是关于Redux/Flux(使用 ReactJS)和动画的主要内容,如果未能解决你的问题,请参考以下文章

第3章 从Flux到Redux

React:如何从 Redux/Flux 操作访问组件引用?

图书推荐React全栈:Redux+Flux+webpack+Babel整合开发

React全栈:Redux+Flux+webpack+Babel整合开发

仅 jQuery 和 ReactJS 动画

ReactJS - 使用反应钩子防止模态的初始动画