草庐IT

javascript - 在React循环中向JSX元素添加 key 的不同方法

coder 2024-05-16 原文

我从事响应工作已经一年多了。我主要是使用.map,.forEach,.filter或如果对象是Object.keys和Object.values来迭代数组。

但是向jsx元素添加唯一键的不同方法是什么。到目前为止,我已经习惯了以下内容

使用数据中的唯一ID作为关键 Prop 的关键:

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map(item => {
    return <span key={item.id}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

使用索引作为关键 Prop 的关键:
const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map((item, i) => {
    let keyValue = i+1;
    return <span key={keyValue}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

除了我上面提到的方法之外,还有没有其他方法可以向jsx元素添加唯一键,并且最有效和推荐的方法是?

最佳答案

首先,避免使用随机键

编写密钥的方法有很多,有些方法的性能会更好。

要了解我们选择的密钥如何影响性能,有必要了解React的对帐算法。

https://reactjs.org/docs/reconciliation.html

tl; dr引入了一种启发式方法,用于比较虚拟DOM树与该VDOM树的n个节点进行比较O(n)。这种启发式方法可以分为以下几点:

  • 不同类型的组件将创建一棵新树:这意味着,在将旧树与新树进行比较时,如果协调程序遇到某个节点确实更改了其类型(例如<Button /><NotButton />),则会导致我们的按钮也要与其子级一起卸载,NotButton也要与其子级一起卸载。
  • 我们可以通过避免重新创建实例来提示React关于如何在VDOM上保留实例。这些提示是由我们提供的键。 :在决定是否应保留节点中的实例(因为其类型保持不变)之后,协调器将迭代该节点的子节点以对其进行比较。

  • 现在假设我们有这个:
    <div>
      <Button title="One" />
      <Button title="Two" />
    </div>
    

    我们想在下一个渲染中向DOM中添加一个Button,例如
    <div>
      <Button title="Zero" />
      <Button title="One" />
      <Button title="Two" />
    </div>
    

    该算法将如下所示:
  • 比较两个VDOM中的<divs>。由于它们具有相同的类型,因此我们无需重新创建新树。 Prop 是相同的,因此此时没有更改可应用于DOM。
  • 按钮OneZero进行比较。协调者检测到这里是 Prop 变更,然后使用此标题更新DOM。
  • 按钮TwoOne进行比较。协调器还会在此处检测 Prop 更改,并使用DOM编写此更改。
  • 检测到新的Button被添加为最后一个 child ,因此在VDOM上创建了一个新的Button实例,并将此更改写入DOM。

  • 请注意,它们在DOM上有很多操作,因为它按索引比较组件。

    现在,我们可以通过告知协调器这些实例应被重用来解决此问题。现在,让我们有这个:
    <div>
      <Button title="One" key="One" />
      <Button title="Two" key="Two" />
    </div>
    

    我们想在下一个渲染中向DOM中添加一个Button,例如
    <div>
      <Button title="Zero" key="Zero" />
      <Button title="One" key="One" />
      <Button title="Two" key"Two" />
    </div>
    

    该算法将如下所示:
  • 比较两个VDOM中的<divs>。由于它们具有相同的类型,因此我们无需重新创建新树。 Prop 是相同的,因此此时没有更改可应用于DOM。
  • 带 child 的第一个 child 。调解员说,这是Button。 “并且有 key ”(“一个”)。然后,在新的子代列表中寻找密钥相同的子代。 “哦,我遇到了!”但协调器意识到的 Prop 没有变化。这样,就不需要DOM操作了。
  • 第二个Button也会发生相同的情况,它将通过keys而不是index进行比较。意识到它是同一个实例,并且没有更改 Prop ,因此React决定不在DOM上应用更改。
  • 对于具有“零”键的Button,由于不存在具有相同键的子级,因此意识到应该在VDOM上创建实例,并且此更改应写在DOM上。

  • 因此,通过可预测的内容使用密钥有助于协调器在DOM上执行较少的操作。健康键是可以从要映射的对象推断出来的键,例如nameidurl(如果我们将urls转换为<imgs />)。

    key=index呢?将没有任何作用,因为默认情况下,协调器会按位置(即其索引)进行比较。

    这些键应该是全局唯一的吗?不必要。这些在 sibling 之间应该是唯一的,因此协调程序可以在节点的子节点进行迭代时区分它们。

    随 secret 钥呢?应不惜一切代价避免这些情况。如果每个渲染上都有一个键更改,这将使React破坏并在VDOM上创建实例(并因此在DOM上进行额外的写操作),因为在新的子级中没有找到带有键的组件,而是一个新的具有相同的类型。

    如果渲染输出像
    <div>
      <Button key={randomGenerator()} />
    </div>
    

    然后,每次执行render时(例如,由于 Prop /状态更改,或者即使重新渲染其父级并且我们的shouldComponentUpdate返回true),也会生成一个新的randomGenerator()密钥。这将像:

    '嘿!我找到了带有Button键的F67BMkd==,但是在下一个中没有找到。我将其删除。
    '哦!我遇到了带有Button键的SHDSA++5!让我们创建一个新的。

    每当协调程序告知应删除和卸载实例时,其内部状态都会丢失;即使我们再次安装它。
    在这种情况下,将不会保留VDOM上的实例。
    Button是相同的,但是协调器在DOM上一团糟。

    希望能帮助到你。

    关于javascript - 在React循环中向JSX元素添加 key 的不同方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52196127/

    有关javascript - 在React循环中向JSX元素添加 key 的不同方法的更多相关文章

    1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

      我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

    2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

      类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

    4. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

      我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

    5. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

      当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

    6. Ruby 方法() 方法 - 2

      我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

    7. ruby - 将 Bootstrap Less 添加到 Sinatra - 2

      我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它

    8. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    9. ruby - Highline 询问方法不会使用同一行 - 2

      设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

    10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    随机推荐