javascript - useState neden yeniden render tetiklemiyor?


Bir dizi olan bir state başlattım ve onu güncellediğimde bile bileşenim tekrar render olmuyor. İşte minimal bir kavram kanıtı:

function App() {
  const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
  console.log("rendering...");
  return (
    <div className="App">
      {numbers.map(number => (
        <p>{number}</p>
      ))}
      <input
        type="text"
        value={numbers[0].toString()}
        onChange={newText => {
          let old = numbers;
          old[0] = 1;
          setNumbers(old);
        }}
      />
    </div>
  );
}

Bu koda dayanarak, girişin başlamak için 0 sayısını içermesi gerektiği ve her değiştirildiğinde durumun da değişmesi gerektiği görülüyor. Girişe "02" girildikten sonra App bileşeni yeniden render edilmiyor. Ancak, onChange fonksiyonuna 5 saniye sonra yürütülen bir setTimeout eklersem, sayıların gerçekten güncellendiğini gösteriyor.

Bileşenin neden güncellenmediğiyle ilgili herhangi bir düşüncen var mı?

İşte kavram kanıtını içeren bir CodeSandbox.

Cevap #1

Sen setNumbers çağırıyorsun ve ona zaten sahip olduğu diziyi geçiriyorsun. Değerlerinden birini değiştirmiş olabilirsin ama hâlâ aynı dizi ve React'in yeniden render etmek için bir sebep görmediğini düşünüyorum çünkü durum değişmedi; yeni dizi eski dizi.

Bunu önlemenin kolay yollarından biri, diziyi yeni bir diziye yaymaktır:

setNumbers([...old])

Cevap #2

numbers öğelerini şu şekilde kopyalamanız gerekiyor: let old=[...numbers];

useState değeri yalnızca değişmişse günceller, yani eğer 44 ise ve 7 olursa günceller. Ama bir dizinin veya nesnenin değişip değişmediğini nasıl anlayabilir? Bu referans ile ilgilidir, bu yüzden let old=numbers yaptığınızda sadece bir referans geçiyorsunuz, yeni bir tane oluşturmuyorsunuz.

Cevap #3

Durumu bu şekilde değiştirebilirsiniz

const [state, setState] = ({})
setState({...state})

veya durumunuz Array ise bunu böyle değiştirebilirsiniz

const [state, setState] = ([])
setState([...state])

Cevap #4

Başkasının zaten teknik çözümü verdi. Bunun neden olduğunu merak edenler için söylemek gerekirse, setSomething() yalnızca önceki ve mevcut durum farklıysa bileşeni yeniden render eder. Javascript'te diziler referans türleri olduğu için, bir dizinin bir öğesini js'te düzenleseniz bile, bu yine de orijinal dizinin referansını değiştirmez. JS açısından bu iki dizi aynıdır, diziler içindeki orijinal içerik farklı olsa bile. Bu yüzden setSomething() eski diziye yapılan değişiklikleri tespit edemez.

Sınıf bileşenlerini kullanırsanız ve durumu setState() ile güncellerseniz, durum değişmiş olsun ya da olmasın bileşenin her zaman güncelleneceğini unutmayın. Bu nedenle, çözüm olarak fonksiyonel bileşeninizi bir sınıf bileşenine dönüştürebilirsiniz. Veya diğerlerinin sağladığı cevapları takip edebilirsiniz.

İlgili makaleler