React'te Üst Bileşenin State'ini Alt Bileşende Değiştirme

Önceki derste, veri state'i üst bileşende saklanıyordu ve alt bileşenler bu verileri prop'lar olarak alıyordu.

Şimdi ürünlerimizi değiştirmek istediğimizi varsayalım. Örneğin, ürünümüzü sepete atacak bir buton yapalım. Başlangıç olarak ürünler dizimize, ürünün sepette olup olmadığını gösteren bir inCart alanı ekleyelim:

jsxconst initProds = [
  {id: id(), name: 'product1', cost: 100, inCart: false},
  {id: id(), name: 'product2', cost: 200, inCart: false},
  {id: id(), name: 'product3', cost: 300, inCart: false},
];

Products bileşeninde, ürün etiketine bir inCart özelliği daha ekleyelim:

jsxfunction Products() {
  const [prods, setProds] = useState(initProds);
  
  const items = prods.map(prod => {
    return <Product
      key   ={prod.id}
      name  ={prod.name}
      cost  ={prod.cost}
      inCart={prod.inCart}
    />;
  });
  
  return <div>
    {items}
  </div>;
}

Şimdi alt Product bileşeninde sepet bilgisini gösteren çıktıyı ve sepete ekleme butonunu yapalım:

jsxfunction Product({ id, name, cost, inCart }) {
  return <div>
    name: <span>{name}</span>,
    cost: <span>{cost}</span>,
    <span>{inCart ? 'in cart' : 'not in cart'}</span>
    <button>to cart</button>
  </div>;
}

Ekleme İşlemini Uygulayalım

React kurallarına göre bir bileşen kendi prop'larını değiştirmemelidir. Bu, alt bileşenin inCart prop'unu değiştirerek kendisini sepete koyamayacağı anlamına gelir. Bu doğru değildir.

Doğrusu, üst bileşenden, kendi prods state'ini değiştirerek belirli bir ürünü sepete koymasını istemek olacaktır.

Bunun nasıl yapıldığına bir bakalım.

Üst bileşende, parametre olarak ürünün id'sini alan ve bu ürün için inCart özelliğini true olarak değiştiren bir addToCart fonksiyonu yapalım:

jsxfunction addToCart(id) {
  setProds(prods.map(prod => {
    if (prod.id === id) {
      prod.inCart = true;
    }
    
    return prod;
  }));
}

Ürün etiketine, oluşturduğumuz fonksiyonu ileteceğimiz bir özellik ve ayrıca ürünün id'sini ileteceğimiz bir özellik daha ekleyelim:

jsxconst items = prods.map(prod => {
  return <Product
    key      ={prod.id}
    id       ={prod.id}
    name     ={prod.name}
    cost     ={prod.cost}
    inCart   ={prod.inCart}
    addToCart={addToCart}
  />;
});

Gördüğünüz gibi, bileşenlerin prop'larına sadece veri değil, fonksiyonlar da iletebilirsiniz.

Products sınıfının son kodları şu şekilde olacaktır:

jsxfunction Products() {
  const [prods, setProds] = useState(initProds);
  
  function addToCart(id) {
    setProds(prods.map(prod => {
      if (prod.id === id) {
        prod.inCart = true;
      }
      
      return prod;
    }));
  }
  
  const items = prods.map(prod => {
    return <Product
      key      ={prod.id}
      id       ={prod.id}
      name     ={prod.name}
      cost     ={prod.cost}
      inCart   ={prod.inCart}
      addToCart={addToCart}
    />;
  });
  
  return <div>
    {items}
  </div>;
}

Şimdi alt sınıfta addToCart fonksiyonu erişilebilir olacak. Butona tıklandığında, ürünün id'sini parametre olarak ileterek bu fonksiyonu çağıralım:

jsxfunction Product({ id, name, cost, inCart, addToCart }) {
  return <div>
    name: <span>{name}</span>,
    cost: <span>{cost}</span>,
    <span>{inCart ? 'in cart' : 'not in cart'}</span>
    <button onClick={() => addToCart(id)}>to cart</button>
  </div>;
}

Sonuç olarak, alt bileşendeki butona tıklandığında, üst bileşenin state'ini değiştirecek olan üst bileşenin fonksiyonu tetiklenecektir. Üst bileşenin state'indeki değişiklik, yeniden oluşturma (re-render) tetikleyecek ve ürünümüzü, değiştirilmiş prop'u ileterek yeniden çizecektir.

Задание 1

Önceki dersteki User bileşenini alın. Kullanıcıyı yasaklamak (ban) için bir buton ekleyin.