我显示了一个 foos 列表,当我点击某个链接时 更多结果 我保留了现有的 foos 并向它们附加了来 self 的 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>
动画.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>
...
有效
那么为什么这种行为在 react 中是这样的呢?
这是一个sandbox基于@Jackyef 代码
最佳答案
这是一个非常有趣的问题。
让我们看看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
总结一下:
- 差异时引用相等性对 React 很重要
- 组件(返回 JSX 的函数或类)应该是稳定的。如果它们在渲染之间发生变化,React 将因第 1 点而遇到困难。
关于javascript - 意外行为 : function vs functional component and animations,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58829050/