代码之家  ›  专栏  ›  技术社区  ›  catandmouse

在React中映射数组时,在何处指示键属性?[重复]

  •  0
  • catandmouse  · 技术社区  · 6 年前

    我正在构建一个React组件,它接受JSON数据源并创建一个可排序表。
    每个动态数据行都有一个唯一的键分配给它,但我仍然得到一个错误:

    阵列中的每个孩子都应该有一个唯一的“钥匙”道具。
    检查TableComponent的渲染方法。

    我的 TableComponent render方法返回:

    <table>
      <thead key="thead">
        <TableHeader columns={columnNames}/>
      </thead>
      <tbody key="tbody">
        { rows }
      </tbody>
    </table>
    

    这个 TableHeader 组件是一行,并且有一个唯一的键分配给它。

    每个 row 在里面 rows 由具有唯一键的组件构建:

    <TableRowItem key={item.id} data={item} columns={columnNames}/>
    

    还有 TableRowItem 看起来像这样:

    var TableRowItem = React.createClass({
      render: function() {
    
        var td = function() {
            return this.props.columns.map(function(c) {
              return <td key={this.props.data[c]}>{this.props.data[c]}</td>;
            }, this);
          }.bind(this);
    
        return (
          <tr>{ td(this.props.item) }</tr>
        )
      }
    });
    

    是什么导致了“唯一钥匙道具”错误?

    0 回复  |  直到 9 年前
        1
  •  41
  •   Penny Liu    4 年前

    您应该为每个孩子添加一个密钥 以及孩子体内的每个元素 .

    这种方法可以处理最小的DOM更改。

    在代码中,每个 <TableRowItem key={item.id} data={item} columns={columnNames}/> 试图让他们里面的孩子没有钥匙。

    检查 this example .

    试着移除 key={i} <b></b> div内的元素(并检查控制台)。

    在示例中,如果我们不提供 <b> 元素,我们想要更新 只有 这个 object.city ,React需要重新渲染整行而不是元素。

    以下是代码:

    var data = [{name:'Jhon', age:28, city:'HO'},
                {name:'Onhj', age:82, city:'HN'},
                {name:'Nohj', age:41, city:'IT'}
               ];
    
    var Hello = React.createClass({
    
        render: function() {
    
          var _data = this.props.info;
          console.log(_data);
          return(
            <div>
                {_data.map(function(object, i){
                   return <div className={"row"} key={i}> 
                              {[ object.name ,
                                 // remove the key
                                 <b className="fosfo" key={i}> {object.city} </b> , 
                                 object.age
                              ]}
                          </div>; 
                 })}
            </div>
           );
        }
    });
    
    React.render(<Hello info={data} />, document.body);
    

    答案发表在 @Chris 下面的内容比这个答案要详细得多。 请看一看 https://stackoverflow.com/a/43892905/2325522

    关于对账关键点重要性的React文档: Keys

        2
  •  5
  •   Nasreen Ustad    3 年前

    迭代数组时要小心!!

    一种常见的误解是,使用数组中元素的索引是抑制可能熟悉的错误的一种可接受的方法:

    Each child in an array should have a unique "key" prop.
    

    然而,在很多情况下,情况并非如此!这是 反模式 那可以进去吗 一些 情况导致 不想要的行为 .


    理解 key 道具

    React使用 钥匙 prop来理解组件到DOM元素的关系,然后用于 reconciliation process 因此,关键在于 总是 残余 唯一的 ,否则,React很可能会混淆元素,并使不正确的元素发生变异。同样重要的是,这些钥匙 保持静止 在所有重新渲染过程中,以保持最佳性能。

    尽管如此,一个人却没有 总是 需要应用上述内容,前提是 已知的 数组是完全静态的。然而,鼓励尽可能采用最佳实践。

    一位发展商在采访中说 this GitHub issue :

    • 关键不在于性能,而在于身份(这反过来会带来更好的性能)。随机分配和更改的值不是身份
    • 在不知道数据是如何建模的情况下,我们无法实际地[自动]提供密钥。如果你没有ID,我建议你使用一些散列函数
    • 我们在使用数组时已经有了内部键,但它们是数组中的索引。插入新元素时,这些键是错误的。

    简而言之,一个 钥匙 应该是:

    • 唯一的 -钥匙不能与钥匙相同 sibling component .
    • 静止的 -关键点不应在渲染之间更改。

    使用 钥匙 道具

    根据上述解释,仔细研究以下示例,并在可能的情况下尝试实施建议的方法。


    坏的(潜在的)

    <tbody>
        {rows.map((row, i) => {
            return <ObjectRow key={i} />;
        })}
    </tbody>
    

    这可以说是在React中迭代数组时看到的最常见错误。这种方法在技术上并不可行 “错了” ,只是。。。 “危险” 如果你不知道自己在做什么。如果你在一个静态数组中进行迭代,那么这是一种非常有效的方法(例如,导航菜单中的链接数组)。但是,如果要添加、删除、重新排序或过滤项目,则需要小心。看看这个 detailed explanation 在官方文件中。

    class MyApp extends React.Component {
      constructor() {
        super();
        this.state = {
          arr: ["Item 1"]
        }
      }
      
      click = () => {
        this.setState({
          arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr),
        });
      }
      
      render() {
        return(
          <div>
            <button onClick={this.click}>Add</button>
            <ul>
              {this.state.arr.map(
                (item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item>
              )}
            </ul>
          </div>
        );
      }
    }
    
    const Item = (props) => {
      return (
        <li>
          <label>{props.children}</label>
          <input value={props.text} />
        </li>
      );
    }
    
    ReactDOM.render(<MyApp />, document.getElementById("app"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="app"></div>

    在这段代码中,我们使用的是一个非静态数组,我们并不局限于将其用作堆栈。这是一种不安全的方法(你会明白为什么)。请注意,当我们将项添加到数组的开头(基本上是取消移位)时,每个项的值 <input> 仍然存在。为什么?因为 钥匙 不能唯一标识每个项目。

    换句话说,一开始 Item 1 key={0} .当我们添加第二项时,最上面的项变成 Item 2 ,然后是 项目1 作为第二项。但是现在 项目1 key={1} 而不是 key={0} 不再相反 项目2 现在有了 key={0} !!

    因此,React认为 <输入> 元素没有改变,因为 Item 带钥匙 0 他总是在顶端!

    那么,为什么这只是一种方法 有时 令人不快的

    只有当数组以某种方式被过滤、重新排列或添加/删除项时,这种方法才有风险。如果它总是静止的,那么使用起来就非常安全。例如,导航菜单 ["Home", "Products", "Contact us"] 可以安全地使用此方法进行迭代,因为您可能永远不会添加新链接或重新排列它们。

    简而言之,你可以的时候到了 安全地 使用索引作为 钥匙 :

    • 阵列是静态的,永远不会改变。
    • 数组永远不会被过滤(显示数组的子集)。
    • 数组永远不会重新排序。
    • 该数组用作堆栈或后进先出。换句话说,添加只能在数组末尾进行(即推送),并且只能删除最后一项(即弹出)。

    如果我们在上面的片段中, 如果将添加的项添加到数组的末尾,则每个现有项的顺序将始终正确。


    非常糟糕

    <tbody>
        {rows.map((row) => {
            return <ObjectRow key={Math.random()} />;
        })}
    </tbody>
    

    虽然这种方法可能会保证密钥的唯一性,但它会 总是 强制反应以重新呈现列表中的每个项目,即使这不是必需的。这是一个非常糟糕的解决方案,因为它会极大地影响性能。更不用说,我们不能排除在以下情况下发生键冲突的可能性: Math.random() 两次生成相同的数字。

    不稳定的关键点(如 数学随机的 )将导致不必要地重新创建许多组件实例和DOM节点,这可能会导致性能下降和子组件中的状态丢失。


    很好

    <tbody>
        {rows.map((row) => {
            return <ObjectRow key={row.uniqueId} />;
        })}
    </tbody>
    

    这可以说是最重要的 best approach 因为它使用的属性对于数据集中的每个项都是唯一的。例如,如果 rows 包含从数据库中提取的数据,可以使用表的主键( 这通常是一个自动递增的数字 ).

    选择键的最佳方法是使用一个字符串,该字符串在其同级中唯一地标识列表项。大多数情况下,您会使用数据中的ID作为密钥


    好的

    componentWillMount() {
      let rows = this.props.rows.map(item => { 
        return {uid: SomeLibrary.generateUniqueID(), value: item};
      });
    }
    
    ...
    
    <tbody>
        {rows.map((row) => {
            return <ObjectRow key={row.uid} />;
        })}
    </tbody>
    

    这也是一个好方法。如果数据集不包含任何保证唯一性的数据( e、 g.任意数的数组 ),有可能发生钥匙碰撞。在这种情况下,最好在迭代数据集中的每个项目之前,手动为其生成唯一标识符。最好是在安装组件或接收数据集时( e、 g.来自 props 或者通过异步API调用 ),只为了做到这一点 一旦 ,而不是每次组件重新渲染时。已经有少数几个库可以为您提供这样的密钥。以下是一个例子: react-key-index .

        3
  •  4
  •   Foyez    3 年前

    这可能对某人有帮助,也可能没有帮助,但它可能是一个快速的参考。这也与上面给出的所有答案类似。

    我有很多使用以下结构生成列表的位置:

    return (
        {myList.map(item => (
           <>
              <div class="some class"> 
                 {item.someProperty} 
                  ....
              </div>
           </>
         )}
     )
             
    

    经过一些尝试和错误(以及一些挫折),在最外层的块中添加一个键属性解决了这个问题。另外,请注意 <> 标签现在被替换为 <div> 现在标记。

    return (
      
        {myList.map((item, index) => (
           <div key={index}>
              <div class="some class"> 
                 {item.someProperty} 
                  ....
              </div>
           </div>
         )}
     )
    

    当然,在上面的例子中,我一直天真地使用迭代索引(index)来填充键值。理想情况下,您应该使用列表项特有的内容。

        4
  •  3
  •   Dharman GPuri    4 年前

    检查:key=undef!!!

    您还收到了警告信息:

    Each child in a list should have a unique "key" prop.
    

    如果你的代码完全正确,但是如果

    <ObjectRow key={someValue} />
    

    有些值未定义!!!请先检查一下。你可以节省时间。

        5
  •  2
  •   Khadim H.    4 年前

    只需添加 唯一密钥 到您的组件

    data.map((marker)=>{
        return(
            <YourComponents 
                key={data.id}     // <----- unique key
            />
        );
    })
    
        6
  •  2
  •   Felipe    4 年前

    警告:数组或迭代器中的每个子级都应该有一个唯一的“键”属性。

    这是一个警告,因为我们将要迭代的数组项需要一个独特的相似性。

    React将组件渲染作为数组进行迭代。

    解决这个问题的更好方法是为要迭代的数组项提供索引。例如:

    class UsersState extends Component
        {
            state = {
                users: [
                    {name:"shashank", age:20},
                    {name:"vardan", age:30},
                    {name:"somya", age:40}
                ]
            }
        render()
            {
                return(
                        <div>
                            {
                                this.state.users.map((user, index)=>{
                                    return <UserState key={index} age={user.age}>{user.name}</UserState>
                                })
                            }
                        </div>
                    )
            }
    

    索引是内置的道具。

        7
  •  2
  •   Arvind    4 年前

    当渲染项没有稳定的ID时,最后可以使用项索引作为键:

    const todoItems = todos.map((todo, index) =>
    // Only do this if items have no stable IDs
       <li key={index}>
          {todo.text}
       </li>
    );
    

    请参阅 List and Keys - React

        8
  •  1
  •   superup    3 年前

    应该为键的每个子项使用唯一的值 tbody 哪里

    • 该值不能为空 相同的 给它的兄弟姐妹
    • 不应在渲染之间更改

    例如,键值可以是 数据库id UUID (Universal Unique Identifier) .

    这里的按键是手动操作的:

    <tbody>
      {rows.map((row) => <ObjectRow key={row.uuid} />)}
    </tbody>
    

    您还可以让React使用 React.Children.toArray

    <tbody>
      {React.Children.toArray(rows.map((row) => <ObjectRow />))}
    </tbody>
    
        9
  •  1
  •   Vik2696    3 年前

    在ReactJS中,如果要呈现一个元素数组,那么每个元素都应该有一个唯一的键。通常情况下,这种情况会产生一个列表。

    例子:

    function List() {
      const numbers = [0,1,2,3];
     
      return (
        <ul>{numbers.map((n) => <li> {n} </li>)}</ul>
      );
    }
    
     ReactDOM.render(
      <List />,
      document.getElementById('root')
    );
    

    在上面的示例中,它使用 li 标签,所以 标记没有唯一的键,因此显示错误。

    修正后:

    function List() {
      const numbers = [0,1,2,3];
     
      return (
        <ul>{numbers.map((n) => <li key={n}> {n} </li>)}</ul>
      );
    }
    
     ReactDOM.render(
      <List />,
      document.getElementById('root')
    );
    

    当您没有唯一密钥时使用map的替代解决方案(react不建议这样做) eslint ):

    function List() {
      const numbers = [0,1,2,3,4,4];
     
      return (
        <ul>{numbers.map((n,i) => <li key={i}> {n} </li>)}</ul>
      );
    }
    
     ReactDOM.render(
      <List />,
      document.getElementById('root')
    );
    

    实例: https://codepen.io/spmsupun/pen/wvWdGwG

        10
  •  1
  •   Vinci    3 年前

    react中定义唯一密钥的最佳解决方案: 在映射中,您初始化了name post,然后key define by key={post.id},或者在我的代码中,您可以看到我定义name项,然后我定义key by key={item.id}:

    <div className="container">
                    {posts.map(item =>(
    
                        <div className="card border-primary mb-3" key={item.id}>
                            <div className="card-header">{item.name}</div>
                        <div className="card-body" >
                    <h4 className="card-title">{item.username}</h4>
                    <p className="card-text">{item.email}</p>
                        </div>
                      </div>
                    ))}
                </div>
        11
  •  0
  •   Dila Gurung    3 年前

    我遇到这个错误消息是因为 <></> 为数组中的某些项返回,而不是 null 需要归还。

        12
  •  -1
  •   A K M Intisar Islam    3 年前

    我有一把独一无二的钥匙,只需要像这样把它当作道具传递:

    <CompName key={msg._id} message={msg} />
    

    这一页很有帮助:

    https://reactjs.org/docs/lists-and-keys.html#keys

        13
  •  -1
  •   thinkerBOB    2 年前

    这是一个警告,但是 解决这一问题将使反应渲染更快 ,

    这是因为 React 需要唯一标识列表中的每个项目。假设列表中某个元素的状态在 Virtual DOM 然后React需要确定哪个元素被更改,以及它在DOM中的什么位置需要更改,以便浏览器DOM与React虚拟DOM同步。

    作为解决方案,只需引入 key 归因于每个人 li 标签这 钥匙 每个元素都应该有一个唯一的值。