javascript - React bileşenlerinde yeniden monte etmeyi nasıl zorlayabilirim?


Diyelim ki koşullu bir render yapan bir görüntü bileşenim var:

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

Girdim şöyle bir şey gibi görünüyor:

class MyInput extends React.Component {

    ...

    render(){
        return (
            <div>
                <input name={this.props.name} 
                    ref="input" 
                    type="text" 
                    value={this.props.value || null}
                    onBlur={this.handleBlur.bind(this)}
                    onChange={this.handleTyping.bind(this)} />
            </div>
        );
    }
}

Diyelim ki employed doğru. Bunu false olarak değiştirdiğimde ve diğer görünüm render edildiğinde, sadece unemployment-duration yeniden başlatılıyor. Ayrıca unemployment-reason, koşul değişmeden önce bir değer verilmişse job-title değerini önceden dolduruyor.

İkinci işleme rutininde işaretlemeyi buna benzer bir şeyle değiştirirsem:

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <span>Diff me!</span>
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

Her şeyin sorunsuz çalışıyor gibi görünüyor. Görünüşe göre React sadece 'job-title' ve 'unemployment-reason' farkını ayıramıyor.

Lütfen bana neyi yanlış yaptığımı söyle.

Cevap #1

Bileşenin anahtarını değiştir.

<Component key="1" />
<Component key="2" />

Bileşen kaldırılacak ve anahtar değiştiği için Bileşen'in yeni bir örneği monte edilecek.

Belgelendi: Muhtemelen Türetilmiş Duruma İhtiyacınız Yok

Bir anahtar değiştiğinde, React mevcut bileşeni güncellemek yerine yeni bir bileşen örneği oluşturur. Anahtarlar genellikle dinamik listeler için kullanılır, ancak burada da faydalıdır.

Cevap #2

Muhtemelen olan şu ki, React yalnızca bir MyInput (unemployment-duration) eklenmiş olduğunu düşünüyor. Bu nedenle, job-title hiç unemployment-reason ile değiştirilmez, bu aynı zamanda önceden tanımlanmış değerlerin de değiştirildiği nedenlerinden biridir.

React farkı hesaplarken, hangi bileşenlerin yeni ve hangi bileşenlerin eski olduğunu key özelliğine göre belirleyecektir. Kodda böyle bir anahtar verilmemişse, kendi anahtarını oluşturacaktır.

Verdiğiniz son kod parçasının çalışmasının nedeni, React’in aslında tüm elementlerin hiyerarşisini üst div altında değiştirmesi gerekmesi ve bunun tüm çocukların yeniden render edilmesini tetikleyeceğini düşünmemdir (bu yüzden çalışıyor). Eğer span’i en üste değil de en alta eklemiş olsaydınız, önceki elementlerin hiyerarşisi değişmeyecek ve bu elementler yeniden render edilmeyecekti (ve sorun devam edecekti).

İşte resmi React belgelerinin söylediği:

Durum, çocuklar karıştırıldığında (arama sonuçlarında olduğu gibi) veya yeni bileşenler listenin önüne eklendiğinde (akışlarda olduğu gibi) daha karmaşık hâle gelir. Her çocuğun kimliği ve durumunun render geçişleri arasında korunması gereken bu durumlarda, her çocuğu bir anahtar atayarak benzersiz şekilde tanımlayabilirsiniz.

React anahtarlanmış çocukları uzlaştırdığında, anahtara sahip herhangi bir çocuğun yeniden sıralanmasını (yok yerine karıştırılmak yerine) veya yok edilmesini (yeniden kullanılmak yerine) sağlayacaktır.

Bunu, benzersiz bir key öğesini ya üst div öğesine ya da tüm MyInput öğelerine kendiniz sağlayarak düzeltebilmelisiniz.

Örneğin:

render(){
    if (this.state.employed) {
        return (
            <div key="employed">
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div key="notEmployed">
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

VEYA

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput key="title" ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" />
                <MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

Şimdi, React farkı yaptığında, divlerin farklı olduğunu görecek ve tüm alt öğeleriyle birlikte yeniden render edecektir (1. örnek). 2. örnekte, job-title ve unemployment-reason üzerinde fark başarılı olacaktır çünkü artık farklı anahtarları vardır.

Elbette istediğiniz anahtarları kullanabilirsiniz, yeter ki benzersiz olsunlar.


Ağustos 2017 Güncellemesi

React'te anahtarların nasıl çalıştığı hakkında daha iyi bir anlayış için, React.js'de benzersiz anahtarları Anlama konusundaki cevabımı okumanızı şiddetle tavsiye ederim.


Kasım 2017 Güncellemesi

Bu güncelleme bir süre önce yayınlanmalıydı, ancak artık ref içinde string literal kullanımı önerilmemektedir. Örneğin ref="job-title" artık yerine ref={(el) => this.jobTitleRef=el} (örneğin) kullanılmalıdır. Daha fazla bilgi için this.refs kullanımı ile ilgili Kullanımdan Kaldırma Uyarısı hakkındaki cevabıma bakın.

İlgili makaleler