Preact渲染的组件错误 [英] Wrong components rendered by Preact

查看:136
本文介绍了Preact渲染的组件错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Preact(用于所有意图和目的,React)来呈现项目列表,保存在状态数组中。每个项目旁边都有一个删除按钮。我的问题是:当点击按钮时,删除了正确的项目(我已经多次验证),但是项目会在最后项目丢失的情况下重新呈现,而删除的项目仍在那里。我的代码(简化):

I'm using Preact (for all intents and purposes, React) to render a list of items, saved in a state array. Each item has a remove button next to it. My problem is: when the button is clicked, the proper item is removed (I verified this several time), but the items are re-rendered with the last item missing, and the removed one still there. My code (simplified):

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案例吗?

What am I doing wrong? Do I need to actively re-render somehow? Is this an n+1 case somehow?

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

Clarification: My problem is not with the synchronicity of state. In the list above, if I elect to remove 'c', the state is updated correctly to ['a','b','d','e'], but the components rendered are ['a','b','c','d']. At every call to removePackage the correct one is removed from the array, the proper state is shown, but a wrong list is rendered. (I removed the console.log statements, so it won't seem like they are my problem).

推荐答案

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

This is a classic issue that is totally underserved by Preact's documentation, so I'd like to personally apologize for that! We're always looking for help writing better documentation if anyone is interested.

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

What has happened here is that you're using the index of your Array as a key (in your map within render). This actually just emulates how a VDOM diff works by default - the keys are always 0-n where n is the array length, so removing any item simply drops the last key off the list.

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

In your example, imagine how the (Virtual) DOM will look on the initial render, and then after removing item "b" (index 3). Below, let's pretend your list is only 3 items long (['a', 'b', 'c']):

以下是初始渲染产生的内容:

Here's what the initial render produces:

<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:

Now when we click "X" on the second item in the list, "b" is passed to removePackage(), which sets state.packages to ['a', 'c']. That triggers our render, which produces the following (Virtual) 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库只知道您在每次渲染时给出的新结构(而不​​是如何更改)从旧结构到新结构),键的作用基本上是告诉它 0 1 项目仍然存在就地 - 我们知道这是不正确的,因为我们希望删除索引 1 的项目。

Since the VDOM library only knows about the new structure you give it on each render (not how to change from the old structure to the new), what the keys have done is basically tell it that items 0 and 1 remained in-place - we know this is incorrect, because we wanted the item at index 1 to be removed.

请记住: key 优先于默认的子差异重新排序语义。在此示例中,因为 key 始终只是基于0的数组索引,所以最后一项( key = 2 )只是被删除,因为它是后续渲染中丢失的那个。

Remember: key takes precedence over the default child diff reordering semantics. In this example, because key is always just the 0-based array index, the last item (key=2) just gets dropped off because it's the one missing from the subsequent render.

所以,要修复您的示例 - 您应该使用标识的内容而不是 offset 作为您的密钥。这可以是项本身(任何值都可以作为键),或 .id 属性(首选,因为它避免散布对象引用,可以阻止GC): / p>

So, to fix your example - you should use something that identifies the item rather than its offset as your key. This can be the item itself (any value is acceptable as a key), or an .id property (preferred because it avoids scattering object references around which can prevent 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>
  );
});






哇,这是多得多的啰嗦我原本打算这样做。


Whew, that was a lot more long-winded that I had intended it to be.

TL,DR:从不使用数组索引(迭代索引)为。最好是它模仿默认行为(自上而下的子重新排序),但更常见的是它只是将所有差异推到最后一个孩子身上。

TL,DR: never use an array index (iteration index) as key. At best it's mimicking the default behavior (top-down child reordering), but more often it just pushes all diffing onto the last child.

编辑 @tommy推荐这个优秀链接到eslint-plugin-react docs ,它解释它比我上面做得更好。

edit: @tommy recommended this excellent link to the eslint-plugin-react docs, which does a better job explaining it than I did above.

这篇关于Preact渲染的组件错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆