我显示一个foos列表,当我单击一些链接更多结果时,我会保留现有的foos,并将它们添加到我的api中,例如波纹管
const [foos, setFoos] = useState([]);
...
// api call with axios
...
success: (data) => {
setFoos([ ...foos, ...data ])
},
每个<Foo />
组件都在上面运行动画
App.js
...
<div className="foos-results">
{ foos.map((foo, index) => <Foo {...{ foo, index }} key={foo.id}/>) }
</div>
...
Foo.js
const Foo = ({ foo, index }) => <div className="circle">...</div>
animation.css
.circle {
...
animation: progress .5s ease-out forwards;
}
问题是当我添加新的动画时,就会为<Foo />
的所有行触发动画。
预期的行为是动画仅针对新动画触发,而不是从现有动画重新开始。
更新
我们已经找到问题的根源(与key={foo.id}
的唯一性无关)
如果我们更改
const Foo = ({ foo, index }) => <div className="circle">...</div>
到
const renderFoo = ({ foo, index }) => <div className="circle">...</div>
将App.js和
...
<div className="foos-results">
{ foos.map((foo, index) => renderFoo({ foo, index })) }
</div>
...
有效
那么为什么这样的行为会发生反应呢?
这是一个基于@Jackyef代码的sandbox
答案 0 :(得分:1)
我认为没有办法禁用此重新渲染动画,但是我认为有一种解决方法可以解决此问题。
我们知道每个div的css每次都会重新加载,因此我能想到的解决方案是创建另一个css类规则(将该类命名为“ circle_without_anim”),并将其css与类“ circle”相同该动画并在添加新div时,紧接在将所有具有类名称“ circle”的div的更改类附加到“ circle_without_anim”之前,这将对以前的div进行更改和css,但没有该动画,并将此新div添加到class “圆”使其成为唯一具有动画效果的div。
正式的算法如下:
结果:由于先前的div的CSS相同但没有动画,因此会给人一种幻觉,即它不会被重新加载,但是新的div具有要重新加载的CSS规则(动画规则)。
答案 1 :(得分:1)
这很有趣。
让我们看看问题中提供的sandbox。
在App
里面,我们可以看到这个。
const renderItems = () => (
<div>
{items.map((item, index) => (
<div className="item" key={item.id}>
<span>
{index + 1}. {item.value}
</span>
</div>
))}
</div>
);
const Items = () => renderItems();
return (
<div className="App">
<h1>List of items</h1>
<button onClick={addItem}>Add new item</button>
<Items />
</div>
);
似乎很无害吧?问题是Items
在App
渲染函数中声明。这意味着在每个渲染器上,Items
实际上是一个不同的函数,即使它所做的是相同的。
<Items />
被转换为React.createElement
,并且在进行差异化时,React考虑每个组件的参照相等性,以决定它是否与先前渲染的组件相同。如果不一样,React会认为它是一个不同的组件,如果不一样,它将只是创建并安装一个新组件。这就是为什么您看到动画再次播放的原因。
如果您在Items
之外声明App
组件,例如:
const Items = ({ items }) => (
<div>
{items.map((item, index) => (
<div className="item" key={item.id}>
<span>
{index + 1}. {item.value}
</span>
</div>
))}
</div>
);
function App() { /* App render function */}
您将看到一切正常。 Sandbox here
因此,总结一下:
答案 2 :(得分:1)
使用以下代码:
const Items = () => renderItems();
...
<Items />
React没有机会知道当前渲染器中的Items
与上一个渲染器中的Items
相同。
考虑一下:
A = () => renderItems()
B = () => renderItems()
A和B是不同的组件,因此,如果您在当前渲染中具有<B />
,而在先前渲染中具有<A />
而不是<B />
,则React将丢弃{{ 1}},然后再次渲染。
您每次渲染都会调用<A />
(因为React.createElement
只是<Items />
的JSX语法糖),因此React在DOM树中剪贴了旧的React.createElement(Items, ...)
并创建了每次都再次。
查看this question了解更多详细信息。
有两种解决方案:
<Items />
组件(如Jackyef建议的那样)Items
代替{ renderItems() }
)