İçeriğe geç
Muhammet Şafak
Arayüz 3 dk okuma

React hooks ile bileşen yazmanın değişen yüzü

React 16.8 ile gelen hooks API'si, sınıf bileşeninden fonksiyon bileşenine geçişi nasıl değiştirdi? Avantajları ve dikkat edilmesi gereken noktalar.


Şubat 2019’da React 16.8 yayınlandı ve hooks kararlı hale geldi. İlk duyuruyu Ekim 2018’deki React Conf’tan beri takip ediyordum; o konuşmada Dan Abramov’un anlattığı problemler — yaşam döngüsü metodlarının dağıttığı mantık, devasa sınıflar, this’in karışıklığı — hepsini tanıdım, hepsini yaşadım. Hooks’u birkaç haftadır gerçek projelerde kullanıyorum ve görüşlerimi paylaşmak istiyorum.

Sınıf bileşeninde ne yoruyordu?

Bir class component yazarken en can sıkıcı kısım, ilgili mantığın farklı lifecycle metodlarına dağılmasıydı. Örneğin bir abonelik başlatmak istiyorsunuz: componentDidMount’ta başlatıyorsunuz, componentWillUnmount’ta kapatıyorsunuz, componentDidUpdate’ta id değişince yeniden başlatıyorsunuz. Bu üç metod üç ayrı yerde, ama hepsi aynı “abonelik yönetimi” mantığına ait.

class UserProfile extends React.Component {
  componentDidMount() {
    this.subscription = userService.subscribe(this.props.userId, this.handleUpdate);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.userId !== this.props.userId) {
      this.subscription.unsubscribe();
      this.subscription = userService.subscribe(this.props.userId, this.handleUpdate);
    }
  }

  componentWillUnmount() {
    this.subscription.unsubscribe();
  }

  handleUpdate = (data) => {
    this.setState({ user: data });
  };

  render() {
    return <div>{this.state.user?.name}</div>;
  }
}

Hooks ile aynı mantık:

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const subscription = userService.subscribe(userId, setUser);
    return () => subscription.unsubscribe();
  }, [userId]);

  return <div>{user?.name}</div>;
}

Mantık bir arada duruyor. useEffect içindeki dönüş fonksiyonu cleanup görevi yapıyor. userId değiştiğinde efekt yeniden çalışıyor. Bu, sınıf varyantının yaptığı şeyin tam karşılığı — ama dağıtılmış değil.

useState ve useEffect: temel ama yeterli

İki hook ile çok geniş bir alan kapsanıyor. useState basit; bir değer ve onu güncelleyen fonksiyon döndürüyor. useEffect ise side effect’leri yönetiyor: veri çekme, abonelikler, timer’lar, DOM işlemleri.

useEffect’in ikinci argümanı olan dependency array kritik bir kavram. Boş bırakırsanız yalnızca ilk render’da çalışır. Bir değer verirseniz o değer değiştiğinde çalışır. Hiç vermezseniz her render’da çalışır — çoğunlukla istemediğiniz şey bu.

// Sadece ilk render'da:
useEffect(() => {
  fetchInitialData();
}, []);

// Her render'da (dikkatli kullanın):
useEffect(() => {
  document.title = `${count} tane öğe`;
});

// count değiştiğinde:
useEffect(() => {
  saveToStorage(count);
}, [count]);

Özel hook’lar: mantığı gerçekten paylaşmak

Hooks’un en değerli yanı, custom hook yazabilmek. use önekiyle başlayan herhangi bir fonksiyon hook olabiliyor; içinde başka hook’lar çağırabiliyorsunuz. Bu, mixin veya Higher-Order Component (HOC) alternatifinden çok daha temiz.

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handler = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);

  return width;
}

// Her bileşende kullanılabilir:
function Header() {
  const width = useWindowWidth();
  return <header>{width > 768 ? <DesktopNav /> : <MobileNav />}</header>;
}

Sınıf dünyasında bu mantığı paylaşmak için HOC veya render prop kullanırdım. İkisi de çalışıyor ama “wrapper hell” yaratıyor.

Dikkat etmem gereken noktalar

Hooks’u kullanmaya başladığımda birkaç tuzakla karşılaştım:

Stale closure sorunu. useEffect içinde props veya state’e erişiyorsanız, bağımlılık dizisine eklemeyi unuttuğunuzda eski değeri okuyabilirsiniz. ESLint’in exhaustive-deps kuralı bunu büyük ölçüde yakalıyor; açık tutmanızı öneririm.

useEffect’i her yan etki için kullanmamak. Bir click handler’ın içinde veri çekmek için useEffect gerekmez — doğrudan event handler’a koyabilirsiniz. useEffect gerçekten render’ın yan etkisi olan şeyler için.

Sınıf bileşenlerini silmek için acele etmemek. React ekibi de söylüyor: sınıf bileşenleri kaldırılmıyor. Mevcut, çalışan sınıf bileşenlerini hooks’a taşımak için vakit harcamak yerine yeni yazılan kodu hooks ile yazmak daha makul.

Sonuç olarak

Hooks, React’i daha “fonksiyonel” bir yere taşıyor. Mantığın yaşam döngüsüne değil, bağlantılı konsepte göre gruplanmasını sağlıyor. Bu değişimi “iyileştirme” olarak görüyorum, devrim olarak değil. Sınıf bileşeninin getirdiği bütün sorunları çözüyor mu? Hayır — ama en yaygın acı noktalarını gerçekten hafifletiyor. Birkaç haftalık kullanımdan sonra geri dönmek istemiyorum.

Yine de yeni bir ekip üyesine hooks öğretirken, önce sınıf bileşenlerinin neyi çözdüğünü anlatmaya özen gösteriyorum. Çünkü hooks’un getirdiği kolaylıklar, ancak ardındaki sorunu görenler için anlam taşıyor. Bir aracı, yerini aldığı şeyi bilmeden kullanmak onu yarım kullanmaktır — ve React gibi hızlı değişen bir ekosistemde bu fark birkaç yıl içinde belirleyici oluyor.

Etiketler: #JavaScript#React
Paylaş:

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi