簡體   English   中英

了解 React.js 中子數組的唯一鍵

[英]Understanding unique keys for array children in React.js

我正在構建一個接受 JSON 數據源並創建可排序表的 React 組件。
每個動態數據行都有一個分配給它的唯一鍵,但我仍然收到以下錯誤:

數組中的每個孩子都應該有一個獨特的“關鍵”道具。
檢查 TableComponent 的 render 方法。

我的TableComponent渲染方法返回:

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

TableHeader組件是單行,並且還分配有一個唯一鍵。

rows row從具有唯一鍵的組件構建的:

<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>
    )
  }
});

是什么導致了唯一的密鑰道具錯誤?

您應該為每個 child以及 children 中的每個元素添加一個鍵。

這種方式 React 可以處理最小的 DOM 更改。

在您的代碼中,每個<TableRowItem key={item.id} data={item} columns={columnNames}/>都試圖在沒有鍵的情況下在其中呈現一些子項。

檢查這個例子

嘗試從 div 內的<b></b>元素中刪除key={i} (並檢查控制台)。

在示例中,如果我們沒有為<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 在底部發布的答案比這個答案更詳細。

React 文檔中關於鍵在對賬中的重要性: Keys

遍歷數組時要小心!

一個常見的誤解是,使用數組中元素的索引是抑制您可能熟悉的錯誤的可接受方式:

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

但是,在很多情況下並非如此! 這是反模式,在某些情況下會導致不需要的行為


了解key道具

React 使用key prop 來理解組件與 DOM 元素的關系,然后將其用於協調過程 因此,密鑰始終保持唯一非常重要,否則 React 很有可能會混淆元素並改變不正確的元素。 為了保持最佳性能,這些鍵在所有重新渲染過程中保持靜態也很重要。

話雖如此,只要知道數組是完全靜態的,並不總是需要應用上述內容。 但是,我們鼓勵盡可能采用最佳實踐。

一位 React 開發人員在這個 GitHub 問題中說:

  • 關鍵不是真正的性能,它更多的是關於身份(這反過來會帶來更好的性能)。 隨機分配和變化的值不是身份
  • 在不知道您的數據是如何建模的情況下,我們實際上無法 [自動] 提供密鑰。 如果您沒有 ID,我建議您使用某種散列函數
  • 我們在使用數組時已經有了內部鍵,但它們是數組中的索引。 當您插入一個新元素時,這些鍵是錯誤的。

簡而言之,一個key應該是:

  • 唯一- 一個鍵不能與同級組件的鍵相同。
  • 靜態- 一個鍵不應該在渲染之間改變。

使用key道具

根據上面的解釋,仔細研究以下示例,並在可能的情況下嘗試實施推薦的方法。


不好(可能)

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

這可以說是在 React 中迭代數組時最常見的錯誤。 這種方法在技術上並不是“錯誤的” ,它只是...... “危險” ,如果你不知道自己在做什么。 如果您正在遍歷靜態數組,那么這是一種完全有效的方法(例如,導航菜單中的鏈接數組)。 但是,如果您要添加、刪除、重新排序或過濾項目,則需要小心。 看看官方文檔中的這個詳細解釋

 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>的值保持不變。 為什么? 因為key不能唯一標識每個項目。

換句話說,首先Item 1key={0} 當我們添加第二個項目時,最上面的項目變成了Item 2 ,然后是Item 1作為第二個項目。 但是,現在Item 1key={1}而不是key={0}了。 相反, Item 2現在有key={0} !!

因此,React 認為<input>元素沒有改變,因為鍵為0Item總是在頂部!

那么為什么這種方法只是有時不好呢?

這種方法只有在以某種方式過濾、重新排列或添加/刪除項目時才有風險。 如果它始終是靜態的,那么使用它是完全安全的。 例如,像["Home", "Products", "Contact us"]這樣的導航菜單可以安全地使用此方法進行迭代,因為您可能永遠不會添加新鏈接或重新排列它們。

簡而言之,這是您可以安全地將索引用作key的時候:

  • 數組是靜態的,永遠不會改變。
  • 數組永遠不會被過濾(顯示數組的子集)。
  • 數組永遠不會重新排序。
  • 該數組用作堆棧或 LIFO(后進先出)。 換句話說,添加只能在數組的末尾進行(即push),並且只能刪除最后一項(即pop)。

如果我們相反,在上面的代碼片段中,將添加的項目到數組的末尾,每個現有項目的順序總是正確的。


很壞

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

雖然這種方法可能會保證鍵的唯一性,但它總是會強制 react 重新渲染列表中的每個項目,即使這不是必需的。 這是一個非常糟糕的解決方案,因為它極大地影響了性能。 更不用說,如果Math.random()兩次產生相同的數字,則不能排除密鑰沖突的可能性。

不穩定的鍵(如Math.random()產生的鍵)會導致許多組件實例和 DOM 節點不必要地重新創建,這可能會導致性能下降和子組件中的狀態丟失。


很好

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

這可以說是 最好的方法,因為它使用的屬性對於數據集中的每個項目都是唯一的。 例如,如果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>

這也是一個很好的方法。 如果您的數據集不包含任何保證唯一性的數據(例如,任意數字的數組),則可能會發生密鑰沖突。 在這種情況下,最好在迭代之前為數據集中的每個項目手動生成一個唯一標識符。 最好在安裝組件或接收數據集時(例如從props或異步 API 調用),以便僅執行一次,而不是每次重新渲染組件時。 已經有一些庫可以為您提供這樣的密鑰。 這是一個示例: react-key-index

這可能會或不會幫助某人,但它可能是一個快速參考。 這也類似於上面提供的所有答案。

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

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)來填充鍵值。 理想情況下,您會使用列表項獨有的東西。

檢查:key = undef !!!

您還收到警告消息:

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

如果您的代碼完整正確,但如果打開

<ObjectRow key={someValue} />

someValue 未定義!!! 請先檢查一下。 您可以節省數小時。

只需將唯一鍵添加到您的組件

data.map((marker)=>{
    return(
        <YourComponents 
            key={data.id}     // <----- unique key
        />
    );
})

您應該為tbody的每個子鍵使用唯一值,其中

  • 該值不能與其兄弟相同(相同)
  • 不應該在渲染之間改變

例如,鍵值可以是數據庫 idUUID(通用唯一標識符)

這里的鍵是手動處理的:

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

你也可以讓 React 使用React.Children.toArray處理鍵

<tbody>
  {React.Children.toArray(rows.map((row) => <ObjectRow />))}
</tbody>

警告:數組或迭代器中的每個孩子都應該有一個唯一的“key”道具。

這是一個警告,因為我們要迭代的數組項需要獨特的相似性。

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>
                )
        }

index 是 React 內置的道具。

當您沒有渲染項目的穩定 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

在 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標簽創建了一個動態列表,因此由於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

在反應中定義唯一鍵的最佳解決方案:在地圖中您初始化名稱 post 然后鍵定義 key={post.id} 或在我的代碼中您看到我定義名稱項目然后我定義鍵 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>

我遇到了這個錯誤消息,因為當需要返回null時,數組中的某些項目會返回<></>

我有一個唯一的密鑰,只需將它作為這樣的道具傳遞:

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

此頁面很有幫助:

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

就我而言,將 id 設置為 tag

<tbody key={i}>

問題已經解決了。

這是一個警告,但解決這個問題將使 Reacts 渲染得更快

這是因為React需要唯一標識列表中的每個項目。 假設如果該列表中某個元素的狀態在 Reacts Virtual DOM中發生了變化,那么 React 需要確定哪個元素發生了變化以及它需要在 DOM 中的哪個位置進行更改,以便瀏覽器 DOM 與 Reacts Virtual DOM 同步。

作為一種解決方案,只需為每個li標簽引入一個key屬性。 key應該是每個元素的唯一值。

var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c, i) {
          return <td key={i}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

這將解決問題。

If you are getting error like :

> index.js:1 Warning: Each child in a list should have a unique "key" prop.

Check the render method of `Home`. See https://reactjs.org/link/warning-keys for more information.

Then Use inside map function like:

  {classes.map((user, index) => (
              <Card  **key={user.id}**></Card>
  ))}`enter code here`

這是一個簡單的例子,我首先使用了&&的反應條件,然后是映射,在我添加了用戶 ID 的key以確保它是唯一的

 <tbody>
                                    {users &&
                                    users.map((user) => {
                                        return <tr key={user._id}>
                                            <td>{user.username}</td>
                                            <td><input
                                                name="isGoing"
                                                type="checkbox"
                                                checked={user.isEnabled}
                                                onChange={handleInputChangeNew}/></td>
                                            <td>{user.role.roleTitle} - {user.role.department.departmentName}</td>
                                            {/*<td className="text-right">
                                                    <Button>
                                                        ACTION
                                                    </Button>
                                                </td>*/}
                                        </tr>
                                    })
                                    }


                                    </tbody>
if we have array object data . then we are map for showing  the data . and pass the unique id (key = {product.id} ) because browser can selected the unique data 

example : [
    {
        "id": "1",
        "name": "walton glass door",
        "suplier": "walton group",
        "price": "50000",
        "quantity": "25",
        "description":"Walton Refrigerator is the Best Refrigerator brand in bv 
         Bangladesh "
    },
    {
        
        "id": "2",
        "name": "walton glass door",
        "suplier": "walton group",
        "price": "40000",
        "quantity": "5",
        "description":"Walton Refrigerator is the Best Refrigerator brand in 
         Bangladesh "
    },
}


now we are maping the data and pass the unique id: 
{
    products.map(product => <product product={product} key={product.id} 
    </product>)
}

這是使用 Key 屬性很好地解釋的 React 文檔,鍵應該在父組件中定義,它不應該在子組件中使用。 反應文檔

在此處輸入圖像描述

在此處輸入圖像描述

由於React 文檔,每一行/項目都應該有一個唯一的鍵。

鍵幫助 React 識別哪些項目已更改、添加或刪除。

就個人而言,我更喜歡使用加密接口來生成隨機 UUID:
crypto內置在 vanilla-js 中)

const numbers = [1, 2, 3, 4, 5];

const listItems = numbers.map((number) =>
  <li key={crypto.randomUUID()}>item {number}
  </li>
);

使用 uuid 庫創建一個隨機 UUID:
運行npm install uuid

import { v4 as uuid } from "uuid";

array.map((item) =>
  <li key={uuid()}>{item}
  </li>
);

我沒有詳細解釋,但這個答案的關鍵是“關鍵”,只需將關鍵屬性放在您的標簽中,並確保每次迭代時都賦予它獨特的價值

#確保鍵的值不會與其他值沖突

例子

<div>
        {conversation.map(item => (
          <div key={item.id  } id={item.id}>
          </div>
        ))}
      </div>

其中對話是一個數組,如下所示:

  const conversation = [{id:"unique"+0,label:"OPEN"},{id:"unique"+1,label:"RESOLVED"},{id:"unique"+2,label:"ARCHIVED"},
   ]

你的密鑰應該是唯一的。就像一個唯一的 id。你的代碼應該是這樣的

<div>
 {products.map(product => (
   <Product key={product.id}>
    </Product>
    ))}
  </div>

視覺解釋。

  1. 不正確的方式 key=index (數組)

在此處輸入圖像描述

如您所見,label 3、label 2 和 label 1全部重新渲染(在元素面板中閃爍)。

  1. 正確的方式key=uniqueId 在此處輸入圖像描述

只有頂部的新元素會閃爍(重新渲染)。

我認為在使用表(或類似示例)時,為了可重用性,創建一個唯一鍵應該從父組件傳遞給子組件。

因為如果你正在創建一個表,那意味着你正在從父級傳遞數據。 如果您分配key={row.name}可能當前數據具有name屬性,但如果您想在其他地方使用此表組件,您假設在您傳遞的每一行數據中,您都具有name屬性。

由於工程師將在父組件中准備數據,因此工程師應根據數據創建密鑰 function。

const keyFunc = (student) => {
    return student.id;
  }; 

在這種情況下,工程師知道它正在發送什么數據,它知道每一行都有唯一的 id 屬性。 也許在不同的數據集中,數據集是股票價格,它沒有“id”屬性,而是“symbol”

 const keyFunc = (stock) => {
        return stock.symbol;
      }; 

這個keyFunc應該作為 prop 傳遞給子組件

“列表中的每個孩子都應該有一個獨特的‘關鍵’道具。” 當您創建沒有特殊 key 屬性的元素列表時,React 中會出現警告。 必須將鍵分配給循環中的每個元素,以便為 React 中的元素提供穩定的標識。

我們可以將 object 的 id 屬性設置為唯一鍵。

export default function App() {
      const posts = [
{ id: 1, title: "First "},
{ id: 2, title: "Second" },
{ id: 3, title: "Third" }
 ];
 return (
<div>
    <ul>
        {posts.map(value => 
            <li key={value.id}>{value.title}</li>
        )}
    </ul>
</div>
 );}


//simple way

//if u using ant design remove the empty fragment...

//worng ans---> change to following crt ans

export default function App() {
      const posts = [
{ id: 1, title: "First "},
{ id: 2, title: "Second" },
{ id: 3, title: "Third" }
 ];
 {fields.map((field,index)=>{
return(
  <> //empty fragment
   <Row key={index}>
    <Col span={6}>hello</Col>
   </Row>
  </>
)
})}

 //correct ans
//remove the empty fragments after solve this key.prop warning problem
export default function App() {
      const posts = [
{ id: 1, title: "First "},
{ id: 2, title: "Second" },
{ id: 3, title: "Third" }
 ];
 {fields.map((field,index)=>{
return(
  <> //empty fragment
   <Row key={index}>
    <Col span={6}>hello</Col>
   </Row>
  </>
)
})}

我遇到了類似的問題,但不准確。 嘗試了所有可能的解決方案,但無法擺脫該錯誤

數組中的每個孩子都應該有一個唯一的“關鍵”道具。

然后我嘗試在不同的本地主機中打開它。 我不知道如何,但它有效!

如果您正在為這個錯誤而苦苦掙扎,列表中的每個孩子都應該有一個唯一的“關鍵”道具。

通過將索引值聲明給呈現元素內的鍵屬性來解決。

App.js 組件

import Map1 from './Map1';

const arr = [1,2,3,4,5];

const App = () => {
  return (
    <>
     
     <Map1 numb={arr} />     

    </>
  )
}

export default App

Map.js 組件

const Map1 = (props) => {

    let itemTwo = props.numb;
    let itemlist = itemTwo.map((item,index) => <li key={index}>{item}</li>)

    return (
        <>        
        <ul>
            <li style={liStyle}>{itemlist}</li>
        </ul>
        </>
    )
}

export default Map1

我為每個鍵使用 Guid 修復了這個問題:生成 Guid:

guid() {
    return this.s4() + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' +
        this.s4() + '-' + this.s4() + this.s4() + this.s4();
}

s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
}

然后將此值分配給標記:

{this.state.markers.map(marker => (
              <MapView.Marker
                  key={this.guid()}
                  coordinate={marker.coordinates}
                  title={marker.title}
              />
          ))}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM