[英]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);
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 1
有key={0}
。 當我們添加第二個項目時,最上面的項目變成了Item 2
,然后是Item 1
作為第二個項目。 但是,現在Item 1
有key={1}
而不是key={0}
了。 相反, Item 2
現在有key={0}
!!
因此,React 認為<input>
元素沒有改變,因為鍵為0
的Item
總是在頂部!
那么為什么這種方法只是有時不好呢?
這種方法只有在以某種方式過濾、重新排列或添加/刪除項目時才有風險。 如果它始終是靜態的,那么使用它是完全安全的。 例如,像["Home", "Products", "Contact us"]
這樣的導航菜單可以安全地使用此方法進行迭代,因為您可能永遠不會添加新鏈接或重新排列它們。
簡而言之,這是您可以安全地將索引用作key
的時候:
如果我們相反,在上面的代碼片段中,將添加的項目推到數組的末尾,每個現有項目的順序總是正確的。
<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
的每個子鍵使用唯一值,其中
例如,鍵值可以是數據庫 id或UUID(通用唯一標識符) 。
這里的鍵是手動處理的:
<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>
);
在 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} />
此頁面很有幫助:
就我而言,將 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>)
}
使用 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>
我認為在使用表(或類似示例)時,為了可重用性,創建一個唯一鍵應該從父組件傳遞給子組件。
因為如果你正在創建一個表,那意味着你正在從父級傳遞數據。 如果您分配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.