gpt4 book ai didi

javascript - Preact 渲染的错误组件

转载 作者:IT王子 更新时间:2023-10-29 03:14:18 24 4
gpt4 key购买 nike

我正在使用 Preact(出于所有意图和目的,React)来呈现保存在状态数组中的项目列表。每个项目旁边都有一个删除按钮。我的问题是:单击按钮时,删除了正确的项目(我多次验证了这一点),但是重新呈现的项目缺少 last 项目,而删除的项目仍然存在。我的代码(简化):

import { h, Component } from 'preact';
import Package from './package';

export default class Packages extends Component {
constructor(props) {
super(props);
let packages = [
'a',
'b',
'c',
'd',
'e'
];
this.setState({packages: packages});
}

render () {
let packages = this.state.packages.map((tracking, i) => {
return (
<div className="package" key={i}>
<button onClick={this.removePackage.bind(this, tracking)}>X</button>
<Package tracking={tracking} />
</div>
);
});
return(
<div>
<div className="title">Packages</div>
<div className="packages">{packages}</div>
</div>
);
}

removePackage(tracking) {
this.setState({packages: this.state.packages.filter(e => e !== tracking)});
}
}

我做错了什么?我需要以某种方式主动重新渲染吗?这是 n+1 的情况吗?

澄清:我的问题不在于状态的同步性。在上面的列表中,如果我选择删除“c”,状态将正确更新为 ['a','b','d','e'],但呈现的组件是['a','b','c','d']。每次调用 removePackage 时,都会从数组中删除正确的包,显示正确的状态,但会呈现错误的列表。 (我删除了 console.log 语句,所以看起来它们不是我的问题)。

最佳答案

这是一个经典的问题,Preact 的文档完全没有解决这个问题,所以我想亲自为此道歉!如果有人感兴趣,我们一直在寻求帮助来编写更好的文档。

这里发生的事情是您使用数组的索引作为键(在渲染中的 map 中)。这实际上只是模拟 VDOM diff 在默认情况下的工作方式——键总是 0-n,其中 n 是数组长度,因此删除任何项目只会删除最后一个键列表。

说明:键超越渲染

在您的示例中,想象一下(虚拟)DOM 在初始渲染中的外观,然后在删除项目“b”(索引 3)后的外观。下面,让我们假设您的列表只有 3 个项目(['a', 'b', 'c']):

这是初始渲染产生的结果:

<div>
<div className="title">Packages</div>
<div className="packages">
<div className="package" key={0}>
<button>X</button>
<Package tracking="a" />
</div>
<div className="package" key={1}>
<button>X</button>
<Package tracking="b" />
</div>
<div className="package" key={2}>
<button>X</button>
<Package tracking="c" />
</div>
</div>
</div>

现在,当我们在列表中的第二项上单击“X”时,“b”将传递给 removePackage(),它将 state.packages 设置为 ['a', 'c']。这会触发我们的渲染,生成以下(虚拟)DOM:

<div>
<div className="title">Packages</div>
<div className="packages">
<div className="package" key={0}>
<button>X</button>
<Package tracking="a" />
</div>
<div className="package" key={1}>
<button>X</button>
<Package tracking="c" />
</div>
</div>
</div>

因为 VDOM 库只知道你在每次渲染时给它的新结构(不知道如何从旧结构更改为新结构),键所做的基本上是告诉它项目 01 保持原样 - 我们知道这是不正确的,因为我们希望删除索引 1 处的项目。

记住:key 优先于默认的子 diff 重新排序语义。在此示例中,因为 key 始终只是从 0 开始的数组索引,所以最后一项 (key=2) 只是被丢弃,因为它是后续项中缺少的一项渲染。

修复

因此,要修复您的示例 - 您应该使用标识 item 而不是其 offset 的内容作为您的 key 。这可以是项目本身(任何值都可以作为键),或 .id 属性(首选,因为它避免分散对象引用,周围可能会阻止 GC):

let packages = this.state.packages.map((tracking, i) => {
return (
// ↙️ a better key fixes it :)
<div className="package" key={tracking}>
<button onClick={this.removePackage.bind(this, tracking)}>X</button>
<Package tracking={tracking} />
</div>
);
});

哇,这比我预想的要冗长得多。

TL,DR:永远不要使用数组索引(迭代索引)作为key。它充其量只是模仿默认行为(自上而下的子项重新排序),但更多时候它只是将所有差异推到最后一个子项上。


编辑: @tommy recommended这个优秀link to the eslint-plugin-react docs ,这比我在上面所做的更好地解释了它。

关于javascript - Preact 渲染的错误组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42773892/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com