You can easily support my projects and me via Github sponsors system! You can follow this link for sponsorship https://github.com/sponsors/barbarbar338
Hooklar React'ın 16.8.0 sürümü ile bizlere tanıtılan birkaç fonksiyondan ibarettir. Amacı ise en kaba tabirle class componentleri ile yapılan her şeyi fonksiyonel componentler ile yaptırabilmeyi sağlamak.
Hookların amacı class componentleri tamamen ortadan kaldırmak değildir ancak günümüz dünyasında class componentler zaten pek de ilgi çeken bir yapı değil. Ancak hook olmazsa da fonksiyonel componentlerde state yönetimi gibi birçok React nimeti çok sıkıntı yaşatır. Sonuç olarak elmizde artık hook API'si bulunmakta!
Hooklar genel olarak React özelliklerini fonksiyonel komponentlerde kullanmanızı sağlayan özel fonksiyonlardır. Örneğin useState hooku, React state'ini bir fonksiyonel componente eklemenizi sağlayan bir hooktur.
Burada örnek olması adına bir state hooku kullanacağız. İlk olarak eskiden yapmanız gereken componenti ve ardından bu makaleden sonra nasıl yapmanız gerektiğini örneklendiren birkaç kod bloğu göreceğiz.
Klasik class component yapısıyla yapılmış bir kod alalım elimize
import React, { Component } from "react";
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
count: 0;
}
}
}
Gördüğünüz gibi componentimiz hem çok uzun hem de göz korkutucu. Basit bir state için oldukça fazla satır yazmamız gerekiyor. Şimdi bir de bu state'i güncellemeye çalışalım:
import React, { Component } from "react";
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
count: 0;
}
}
render() {
return (
<div>
<span>{this.state.count} defa tıklandı.
<button onClick={
() => this.setState({ count: this.state.count + 1})
}>
Tıkla
</button>
</div>
);
}
}
Gördüğünüz gibi kod karışmaya başladı bile. Artık hook örneğine geçebiliriz.
import React, { useState } from "react";
const MyComponent = () => {
const [count, setCount] = useState(0);
}
Gördüğünüz gibi hem hızımızı arttırdık hem de düzenimizi. Ayrıca artık daha rahat bir state yönetimine sahibiz.
Şimdi bir de bu state'i güncellemeye çalışalım:
import React, { useState } from "react";
const MyComponent = () => {
const [count, setCount] = useState(0);
return (
<div>
<span>{count} defa tıklandı.
<button onClick={
() => setCount(count + 1)
}>
Tıkla
</button>
</div>
);
}
Gördüğünüz üzere kodumuzda inanılmaz bir kısalma var ve artık daha rahat okunabilir halde.
Hooklar her ne kadar JavaScript fonksiyonları olsa da bu fonksiyonları şu iki temel kurala uyarak kullanmanız gerekmektedir:
useState hooku klasik class componentlerindeki state özelliğini fonksiyonel componentimize eklememizi sağlayan bir hooktur.
import React, { useState } from "react";
const MyComponent = () => {
// useState kullanım örneği
const [count, setCount] = useState(0);
}
Componentimizde bir değişiklik olduğunda tekrar renderlanmasını istiyorsak kesinlikle kullanmamız gereken bir hooktur. Basitçe hooku anlamak istersek hooku çağırırken girdiğimiz argüman state'imizin başlangıç değeri olacaktır. Yukarıdaki örnekte count
state'inin varsayılan değeri olarak 0
ayarladık. Yani componentimiz ilk renderlandığında count
değeri 0
olacaktır.
Hookumuzu çağırdıktan sonra bize 2 elementten oluşan bir array verecektir.
const [count, setCount] = useState(0);
Bu arrayın ilk elementi bizim state'imizi, ikincisi ise bu state'i güncellememizi sağlayacak fonksiyonu barındırır. State'imizi güncellemek istediğimizde bu fonksiyonu çağırmamız lazım.
const [count, setCount] = useState(0);
count // >> 0
setCount(3);
count // >> 3
Eğer const
, let
veya var
kullanarak bir değer oluşturursak bu değerleri ne kadar güncellersek güncelleyelim componentimiz yeniden renderlanmaz ve güncel veriyi göremeyiz.
import React, { useState } from "react";
const MyComponent = () => {
// doğru kullanım
const [count, setCount] = useState(0);
// yanlış kullanım
let count = 0;
}
useEffect hookunun 4 temel işlevi vardır. Bu hooku genel olarak klasik class componentlerinde bulunan componentDidMount
, componentDidUpdate
ve componentWillUnMount
metotlarının yerini tutması için kullanırız. 4. işlevi ise spesifik bir state güncellenmesini takip etmektir.
Componentimiz her renderlandığında bir kod çalıştırmamızı sağlayan bir metottur.
import React, { Component } from "react";
class MyComponent extends Component {
componentDidUpdate() {
console.log("Componentimiz yeniden renderlandı.");
}
}
Bu özelliği fonksiyonel componentler ile şu şekilde yapıyoruz:
import React, { useEffect } from "react";
const MyComponent = () => {
useEffect(() => {
console.log("Componentimiz yeniden renderlandı.");
});
}
useEffect hooku gördüğünüz gibi herhangi bir veri returnlamıyor. Yani sadece componentimizin renderlanması sonucu yapmamız gereken değişimleri yönettiğimiz bir hooktur.
Componentimiz ilk kez renderlandığında sadece 1 kez çalışmasını istediğimiz bir metottur. Örneğin bir websocket bağlantısı yapacaksınız veya bir interval oluşturacaksınız. Bunları component her renderlandığında tekrar tekrar yaparsanız veriler birbirine karışır. Sonuç olarak sadece 1 kere yapmak istediğimiz için bu işlemleri componentDidMount
içinde yaparız.
import React, { Component } from "react";
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
interval: undefined,
};
}
componentDidMount() {
const i = setInterval(() => {
console.log("Bu mesaj her 10 saniyede bir loglanacaktır.");
}, 1000 * 10);
this.setState({ interval: i });
console.log("Componentimiz ilk kez renderlandı ve bu fonksiyon bir daha çağırılmayacaktır.");
}
}
Bu özelliği fonksiyonel componentler ile şu şekilde yapıyoruz:
import React, { useState, useEffect } from "react";
const MyComponent = () => {
const [interval, setIntervalState] = useState();
useEffect(() => {
const i = setInterval(() => {
console.log("Bu mesaj her 10 saniyede bir loglanacaktır.");
}, 1000 * 10);
setIntervalState(i);
console.log("Componentimiz ilk kez renderlandı ve bu fonksiyon bir daha çağırılmayacaktır.");
// ⋁⋁⋁ buraya dikkat ediniz
}, []);
}
Gördüğünüz üzere useEffect hookumda boş bir array belirttim. Burayı şimdilik kafanıza takmayın, birazdan onun amacını tam anlamıyla anlatacağım.
Böylece fonksiyonel componentimizde de componentDidMount
metodunu çağırmış olduk.
Componentimiz görüş alanımızdan çıkacağında (yani renderlanmayacağında, dokümanımızdan kaldırılacağında) çağırılan bir metottur. Bu metot sayesinde son temizlikleri yapabilir ve performansı arttırabiliriz. Bu metodu daha iyi anlatabilmek için yukarıdaki componentDidMount
örneğini biraz geliştireceğim.
import React, { Component } from "react";
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
interval: undefined,
};
}
componentWillUnMount() {
// Son temizliğimizi yaptık ve artık performans sıkıntısı yaşamayacağız.
clearInterval(this.state.interval);
console.log("Component dokümandan kaldırıldı.");
}
componentDidMount() {
const i = setInterval(() => {
console.log("Bu mesaj her 10 saniyede bir loglanacaktır.");
}, 1000 * 10);
this.setState({ interval: i });
console.log("Componentimiz ilk kez renderlandı ve bu fonksiyon bir daha çağırılmayacaktır.");
}
}
Bu özelliği fonksiyonel componentler ile şu şekilde yapıyoruz:
import React, { useState, useEffect } from "react";
const MyComponent = () => {
const [interval, setIntervalState] = useState();
useEffect(() => {
const i = setInterval(() => {
console.log("Bu mesaj her 10 saniyede bir loglanacaktır.");
}, 1000 * 10);
setIntervalState(i);
console.log("Componentimiz ilk kez renderlandı ve bu fonksiyon bir daha çağırılmayacaktır.");
// burası "componentWillUnMount" metodu
return () => {
// Son temizliğimizi yaptık ve artık performans sıkıntısı yaşamayacağız.
clearInterval(interval);
console.log("Component dokümandan kaldırıldı.");
}
}, []);
}
useEffect
hooku içerisinde bir fonksiyon returnlarsak bu fonksiyon, componentimiz dokümandan kaldırılırken çağırılacaktır.
useEffect
hookundaki favorim. Bu özellik sayesinde sadece belirttiğimiz state'te bir değişim olduğu zaman bir işlem yapabiliyoruz.
import React, { useState } from "react";
const MyComponent = () => {
const [count, setCount] = useState();
const [name, setName] = useState();
}
Örneğin yukarıda 2 adet state verimiz bulunmakta ancak bir sadece name
state'i değiştiği zaman bir işlem yapmak istiyoruz. O zaman işin içine dependency array
dediğimiz bir olay giriyor. Yukarıda useEffect
'in componentDidMount
karşılığını anlatırken boş bir array belirtmiştim. Burası tam da onunla ilgili.
useEffect
hookunu çağırırken ilk parametre olarak effect fonksiyonunu belirtiyoruz, bunu geçtiğimiz birkaç örnekte anladık. Ancak useEffect
hookuna 2 adet parametre tanımlayabiliriz. İkinci parametre ise dependency array
olarak geçiyor. Bu parametre bir array ([]
) olmak zorunda. Bu array içine koyduğumuz veriler hookumuzun dependency'si oluyor. Yani bu verilerde bir güncellenme olduğu zaman hookumuz tekrar çalıştırılıyor.
import React, { useState, useEffect } from "react";
const MyComponent = () => {
const [count, setCount] = useState();
const [name, setName] = useState();
useEffect(() => {
console.log("Bu hook componentimiz her renderlandığında çalışacaktır.");
});
useEffect(() => {
console.log("Bu hook yalnızca 1 kere (ilk renderdan sonra) çalışacaktır.");
}, []);
useEffect(() => {
console.log("Bu hook yalnızca 'name' verisi güncellenince çalışacaktır.");
}, [name]);
}
Yani dependency array'i boş bırakmak demek bu hooku yalnızca 1 kere (ilk renderdan sonra) çalışmak üzere ayarlamak demek oluyor.
Hooklar bu yazının başında da bahsettiğim gibi birer özel JavaScript fonksiyonlarıdır. Bir fonksiyon içinde başka fonksiyonlar da çağırabilir veya aynı kodu birden çok yerde kullanıyorsak bunu fonksiyon haline getirip işimizi kolaylaştırabiliriz. Yani bu demek oluyor ki fonksiyonlar için geçerli olan her şey hooklar için de geçerlidir. Bir hook içinde başka hooklar da çağırabilir ve aynı hooku birden çok yerde kullanabiliriz.
Şimdi basit bir hook yapmaya çalışalım. Bu yapacağımız hookun amacı belirttiğimiz URL'ye bir request atması ve bu requestin sonucunu vermesi olsun.
İlk olarak temel bir hook yapısı oluşturalım:
import React, { useState } from "react";
export const useFetch = (url) => {
const [body, setBody] = useState();
const [error, setError] = useState();
return [body, error];
}
Gördüğünüz gibi hookumuzun içerisinde de useState
hookunu kullandık. Böylece bir veri güncellemesi yaptığımızda componentimiz tekrar renderlanacaktır. Şimdi request özelliğini entegre edelim:
import React, { useState, useEffect } from "react";
export const useFetch = (url) => {
const [loading, setLoading] = useState(true);
const [body, setBody] = useState();
const [error, setError] = useState();
const fetcher = async () => {
setLoading(true);
const res = await fetch(url);
if (!res.ok) {
setError(res.statusText);
setLoading(false);
return;
}
const body = await res.json();
setBody(body);
setError();
setLoading(false);
}
useEffect(fetcher, []);
return [loading, body, error, fetcher];
}
Ve hookumuz tamamlandı! Fetcher fonksiyonu ile bir fetch yapıyoruz ve hata varsa setError
ile hata state'ini güncelliyoruz, yoksa setBody
ile body state'ini güncelliyoruz. Bu hooku artık şu şekilde kullanabiliriz:
import React, { useState } from "react";
import { useFetcher } from "./path/to/useFetcher";
const MyComponent = () => {
const [loading, body, error] = useFetcher("https://pokeapi.co/api/v2/pokemon/ditto");
return loading ? (
<div>Yükleniyor...</div>
) : error ? (
<div>Bir hata oluştu.</div>
) : (
<div>Pokemonun adı: {body?.name}</div>
)
}
Yazıyı buraya kadar okuduysanız artık hookların ne demek olduğunu, nasıl çalıştıklarını ve kendi hookunuzu nasıl yapabileceğinizi anlamışsınızdır demektir. Zaman ayırdığınız için teşekkürler, başka bir yazıda görüşmek üzere.