Diziler üzerinde yineleme yaparken dikkatli olun !!
Dizideki öğenin dizinini kullanmanın, muhtemelen aşina olduğunuz hatayı bastırmanın kabul edilebilir bir yolu olduğu yaygın bir yanlış anlamadır:
Each child in an array should have a unique "key" prop.
Ancak, birçok durumda değil! Bu anti-desen içinde kutu olduğunu bazı durumlara yol istenmeyen davranış .
Anlamak key
pervane
React key
, daha sonra mutabakat sürecinde kullanılan bileşen-DOM Öğesi ilişkisini anlamak için pervaneyi kullanır . Bu nedenle, anahtarın her zaman benzersiz kalması çok önemlidir , aksi takdirde React'ın öğeleri karıştırması ve yanlış olanı değiştirmesi için iyi bir şans vardır. En iyi performansı korumak için bu tuşların tüm yeniden oluşturma işlemlerinde statik kalması da önemlidir .
Bununla birlikte , dizinin tamamen statik olduğu biliniyorsa , yukarıdakilerin her zaman uygulanması gerekmez . Ancak, mümkün olan en iyi uygulamaların uygulanması teşvik edilir.
Bir Tepki geliştiricisi bu GitHub sayısında şunları söyledi :
- anahtar gerçekten performansla ilgili değil, daha çok kimlikle ilgilidir (bu da daha iyi performans sağlar). rastgele atanan ve değişen değerler kimlik değildir
- Verilerinizin nasıl modelleneceğini bilmeden gerçekçi bir şekilde [otomatik] anahtarlar sağlayamayız. Kimlikleriniz yoksa belki de bir çeşit karma işlevini kullanmanızı öneririm
- Dizileri kullandığımızda zaten dahili anahtarlarımız var, ancak bunlar dizideki dizin. Yeni bir öğe eklediğinizde, bu tuşlar yanlış olur.
Kısacası, a şöyle key
olmalıdır:
- Benzersiz - Anahtar, kardeş bileşeninkiyle aynı olamaz .
- Statik - Bir anahtar, oluşturma işlemleri arasında asla değişmemelidir.
key
Pervaneyi kullanma
Yukarıdaki açıklamaya göre, aşağıdaki örnekleri dikkatlice inceleyin ve mümkünse önerilen yaklaşımı uygulamaya çalışın.
Kötü (Potansiyel Olarak)
<tbody>
{rows.map((row, i) => {
return <ObjectRow key={i} />;
})}
</tbody>
Bu, React'ta bir dizi üzerinden yineleme yaparken görülen en yaygın hatadır. Bu yaklaşım teknik olarak "yanlış" değil , ne yaptığınızı bilmiyorsanız sadece ... "tehlikeli" dir . Statik bir dizi üzerinden yineleme yapıyorsanız, bu tamamen geçerli bir yaklaşımdır (örneğin, navigasyon menünüzdeki bir bağlantı dizisi). Ancak, öğeleri ekler, kaldırır, yeniden sıralar veya filtreliyorsanız dikkatli olmanız gerekir. Resmi belgelerdeki bu ayrıntılı açıklamaya bir göz atın .
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>
Bu snippet'te statik olmayan bir dizi kullanıyoruz ve kendimizi bunu yığın olarak kullanmakla kısıtlamıyoruz. Bu güvensiz bir yaklaşımdır (nedenini göreceksiniz). Dizinin başına nasıl öğe eklediğimizi (temelde unshift), her birinin değerinin <input>
yerinde kaldığını unutmayın. Neden? Çünkü key
her öğeyi benzersiz şekilde tanımlamaz.
Başka bir deyişle, ilk Item 1
sahiptir key={0}
. İkinci öğe eklediğinizde, üst öğe olur Item 2
, bunu takiben Item 1
ikinci öğe olarak. Ancak, şimdi Item 1
var key={1}
ve key={0}
artık değil. Bunun yerine, Item 2
şimdi var key={0}
!!
Bu nedenle, React <input>
öğelerin değişmediğini düşünüyor , çünkü Item
with tuşu 0
her zaman en üstte!
Peki bu yaklaşım neden sadece bazen kötü?
Bu yaklaşım yalnızca dizi bir şekilde filtrelenir, yeniden düzenlenirse veya öğeler eklenirse / kaldırılırsa risklidir. Her zaman statikse, kullanımı tamamen güvenlidir. Örneğin, böyle bir gezinme menüsü ["Home", "Products", "Contact us"]
bu yöntemle güvenli bir şekilde tekrarlanabilir, çünkü muhtemelen hiçbir zaman yeni bağlantılar eklemeyecek veya yeniden düzenlemeyeceksiniz.
Kısacası, dizini güvenli bir şekilde şu şekilde kullanabilirsiniz key
:
- Dizi statiktir ve asla değişmez.
- Dizi asla filtrelenmez (dizinin bir alt kümesini görüntüler).
- Dizi asla yeniden sıralanmaz.
- Dizi bir yığın veya LIFO (son giren ilk çıkar) olarak kullanılır. Başka bir deyişle, ekleme sadece dizinin sonunda yapılabilir (yani itme) ve sadece son öğe kaldırılabilir (yani pop).
Bunun yerine, yukarıdaki snippet'te eklenen öğeyi dizinin sonuna itmiş olsaydık, mevcut her bir öğenin sırası her zaman doğru olurdu.
Çok kötü
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
Bu yaklaşım muhtemelen anahtarların benzersizliğini garanti edecek olsa da, gerekli olmasa bile listedeki her öğeyi yeniden oluşturmaya her zaman tepki vermeye zorlayacaktır. Performansı büyük ölçüde etkilediği için bu çok kötü bir çözüm. Math.random()
Aynı sayıyı iki kez üreten olayda anahtar çarpışma olasılığını dışlayamayacağından bahsetmiyoruz bile .
Kararsız anahtarlar (tarafından üretilenler gibi Math.random()
) birçok bileşen örneğinin ve DOM düğümünün gereksiz yere yeniden oluşturulmasına neden olarak performans düşüşüne ve alt bileşenlerde kaybolmaya neden olabilir.
Çok iyi
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uniqueId} />;
})}
</tbody>
Bu, tartışmasız en iyi yaklaşımdır çünkü veri kümesindeki her öğe için benzersiz bir özellik kullanır. Örneğin, rows
bir veritabanından getirilen veriler içeriyorsa, tablonun Birincil Anahtarı ( genellikle otomatik olarak artan bir sayıdır ) kullanılabilir.
Bir anahtar seçmenin en iyi yolu, kardeşleri arasında bir liste öğesini benzersiz şekilde tanımlayan bir dize kullanmaktır. Çoğu zaman verilerinizdeki kimlikleri anahtar olarak kullanırsınız
İyi
componentWillMount() {
let rows = this.props.rows.map(item => {
return {uid: SomeLibrary.generateUniqueID(), value: item};
});
}
...
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uid} />;
})}
</tbody>
Bu da iyi bir yaklaşım. Veri kümeniz benzersizliği garanti eden herhangi bir veri içermiyorsa ( örneğin, rasgele sayılar dizisi ), önemli bir çarpışma olasılığı vardır. Bu gibi durumlarda, veri kümesindeki her öğe için tekrarlamadan önce manuel olarak benzersiz bir tanımlayıcı oluşturmak en iyisidir. Tercihen komponenti monte ederken veya veri seti alındığında ( örneğin props
bir async API çağrısından veya async API çağrısından ), bunu bileşen her yeniden oluşturuşunda değil, yalnızca bir kez yapmak için. Zaten size bu tür anahtarları sağlayabilecek bir avuç kütüphane var. İşte bir örnek: tepki anahtarı dizini .
key
özelliği olmalıdır . ReactJS'nin uygun DOM düğümlerine referanslar bulmasına ve yalnızca işaretleme içindeki içeriği güncellemesine yardımcı olur, ancak tüm tabloyu / satırı yeniden oluşturmaz.