当前位置: 首页 > 面试题库 >

Preact渲染的组件错误

花欣然
2023-03-14
问题内容

我正在使用Preact(出于所有意图和目的,React)来渲染保存在状态数组中的项目列表。每个项目旁边都有一个删除按钮。我的问题是:单击该按钮时,将删除正确的项目(我已对此进行了几次验证),但是重新渲染项目时缺少
最后一个 项目,并且删除的项目仍然存在。我的代码(简体):

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的文档完全没有解决这个问题,因此我想为此道歉!如果有人感兴趣,我们一直在寻求帮助以编写更好的文档。

这里发生的事情是您将Array的索引用作键(在render中的地图中)。这实际上只是一个模拟的虚拟域差异是如何工作的默认值-
键始终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)会被丢弃,因为它是后续渲染中缺少的一项。

修复

因此,要修正您的示例-您应该使用可以识别 项目 而不是其 偏移量
的键作为键。这可以是项目本身(任何值都可以作为键),也可以是.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>
  );
});

ew,这比我原本打算的要长得多。

TL,DR:
切勿将数组索引(迭代索引)用作key。充其量,它模仿的是默认行为(自上而下的子级重新排序),但更多情况下,它只是将所有差异推送到最后一个子级。

编辑:
@tommy建议将此链接链接到eslint-
plugin-react docs,这比我上面做的要好。



 类似资料:
  • 我在Java中的JLabel存在一个渲染问题:我使用oberver observable模式,当我的模型通知我的视图JLabel已经更改了JLabel的内容,或者特别是在我的JLabel区域中显示的内容是随机的时。有时它渲染另一个面板中按钮的一部分,有时它渲染我在视图的其他组件中设置的颜色!但是,如果我最小化然后最大化我的帧,所有的渲染都是正确的。 对不起,我的英语不好。 EDIT这是我的JPan

  • 问题内容: 我尝试运行RichFaces4应用,但组件未渲染。例如,当我尝试这个演示:演示时,我得到如下信息: 我的代码与演示中的代码几乎相同。我刚刚添加了表单标签,因为它对此有所抱怨。 问题答案: 这就是Crome开发人员工具告诉我的内容 http://img571.imageshack.us/i/rfnotdefined.jpg (未捕获的ReferenceError:未定义RichFaces

  • This group contains all Components that have to do with rendering in-game and user interface elements. Lighting and special effects are also included in this group. 这个组包括在游戏中渲染和界面元素的所有组件。光照和特定效果也包含在这个

  • Sprite 组件参考 Label 组件参考 Mask 组件参考 Graphics 组件参考 RichText 组件参考 UIStaticBatch 组件参考

  • 我对react还比较陌生,我一直在分解一个web应用程序,并用react组件替换部分。我现在正在开发一个组件,其中包含我创建的几个不同组件。 在新组件中,我在componentDidMount函数中进行API调用,并创建子组件。乍一看,一切看起来都很完美,但当我们在其中一个子组件中进行状态更改,然后在父组件中进行更改时,子组件将其状态重置为更改之前的状态。 我知道发生了什么,州政府没有被传递给家长

  • Vue中是否有一个事件在元素重新渲染后被触发?我有一个更新的对象,这导致我的模板更新。在它更新之后,我想触发一个运行jQuery函数的事件。代码如下所示: 如您所见,我尝试使用监视事件,但是当监视事件触发时,我的页面尚未更新。然而,blogPost已更改,但vue仍在呈现页面。我知道理论上我可以通过添加setTimeout来解决这个问题,但我更喜欢更干净的东西。