ReactJS Kancalar (Hooks)

Herkese selam!

React hook'ları, React uygulamalarında vazgeçilmez araçlar haline gelmiştir. useState, useEffect, useContext gibi önemli hook'lar, component'ların durum yönetimi, yan etkilerin yönetimi, veri paylaşımı gibi temel işlevlerin yürütülmesinden sorumludur. Bu yazıda sıkça kullanılan bazı hook'lardan bahsedeceğim ve örnekler ile açıklamaya çalışacağım. Keyifli okumalar :).


React Hooks Nedir?

React v16.7.0-alpha sürümü ile hayatımıza giren React hooks, bir class yazmadan React özelliklerini kullanmamıza olanak sağlayan bir yapı olarak karşımıza gelmektedir. Hook'lar geliştiricileri Class Component'ların karmaşıklığından kurtarırken, JavaScript'teki fonksiyonel yapıyı geliştiricilerin daha rahat kullanmasına olanak sağlar. Detaylı bir şekilde incelemek isteyen kullanıcılar buraya tıklayarak ReactJS'in güncel dokümantasyonundan yararlanabilirler.

Birçok hook çeşidi olmakla beraber bunlardan en sık kullanılanları: useState, useEffect, useContext, useRef, useCallback, useMemo olarak listelenebilir. Bunların dışında geliştiriciler ihtiyaç halinde kendi Custom Hook'larını yazabilmektedir. Bu da React'in etkili bir şekilde kullanılmasında büyük bir esneklik sağlamaktadır. Fazla uzatmadan useState Hook ile yumuşak bir başlangıç yapalım.


1) useState Hook

React functional component'larda useState durum yönetimi için kullanılır.

State yönetimini yerine getirir ve functional component'lar üzerinde state tutma özelliği kazandırır. useState, değişkenleri global bir şekilde tuttuğundan kaybolmaları gibi bir durum söz konusu değildir. Component'ların durumu değiştiğinde tekrar render edilmelerinden ve güncel verileri ekrana yansıtmalarından sorumludur. Örnek verecek olursak:

import React, { useState } from "react";

const Counter = () => {
 const [counter, setCounter] = useState(0);

 return (
   <>
	 <p>Count: {counter}</p>
	 <button onClick={() => setCounter(counter + 1)>Artır</button>
	 <button onClick={() => setCounter(counter - 1)>Azalt</button>
	</>
  );
};

export default Counter;

Yukarıdaki örnekte useState hook'u kullanarak "counter" adında bir state tanımladık. Initial state (başlangıç durumu) olarak 0 değerini almasını söyledik ve ardından "setCounter" fonksiyonunu kullanarak oluşturduğumuz state'i güncellenebilir hale getirdik. "Artır" ve "Azalt" butonlarına tıklandığında "setCounter" fonksiyonu yardımıyla "counter" değerini artırabiliriz ya da azaltabiliriz. React, state'in değiştiğini algılayarak otomatik olarak component'ı günceller.


Bu bilgilere ek olarak eğer bir form verisi için state yönetimi sağlıyorsanız ve bu form submit event alıyorsa ya da fetch isteğinde bulunacaksanız aşağıdaki şekilde useState hook'u kullanabilirsiniz:

// Normalde state'leri tanımlarken bu kısımdaki gibi olmasını bekliyorduk
const [name, setName] = useState("");
const [mail, setMail] = useState("");
const [password, setPassword] = useState("");

// Yukarda bahsettiğimiz koşulların sağlandığı durumlarda bu kısımdaki gibi kullanılabilir
const [userInfo, setUserInfo] = {
   name: "",
   mail: "",
   password: "",
};

Daha farklı kullanımlar ve farklı örnekler için kompleks projelerin kaynak kodlarına göz atabilirsiniz.

Özetle useState hook, functional component'larda durum yönetimini sağlamak ve durum ile dinamik olarak etkileşime geçmek için kullanılır.


2) useEffect Hook

React functional component'larda yan etkileri işlemek için kullanılır. Bu kısımda bahsedilen yan etkiler, component'ların render edilmesi sırasında meydana gelen olaylar veya işlemler olarak nitelendirilebilir. Örnek olarak API çağrıları ve belirli durumlar verilebilir. useEffect sayesinde bu yan etkiler, component'ın yaşam döngüsü boyunca yönetilebilir. useEffect hook'a örnek olarak:

import React, { useState } from "react";

const ExampleComponent = () => {
   const [data, setData] = useState([]);

   useEffect(() => {
      fetchData().then((response) => setData(response.data));
   }, []);

   useEffect(() => {
      console.log(data);
   }, [data]);

   return (
      <>
         <div>
            <ul>
               {data.map((item) => (
                  <li key={item.id}>{item.name}</li>
               ))}
            </ul>
         </div>
      </>
   );
};

export default ExampleComponent;

Yukarıdaki örnekte useState hook'u kullanarak API üzerinden gelen response'u tutabileceğimiz bir state tanımladık. Ardından useEffect hook ile API üzerine bir istekte bulunduk ve dönen response'u "data" state'ine tanımladık. Bu isteği atarken useEffect hook'una verdiğimiz ikinci parametre olan boş array ([]), etkinin sadece component'ın ilk render edildiğinde çalışmasını sağlar.

İkinci örnekte ise useEffect için "data" state'ini bağımlılık olarak belirttik; bu sayede useEffect hook'un "data" state'i her güncellendiğinde tekrar çalışması sağlandı. Component'ın kalan kısmında ise "data" state'inden gelen değerler bir tablo üzerinde render edildikten sonra kullanıcıya sunulmaktadır.


Şimdi useEffect'i daha farklı bir kullanım örneği ile netleştirelim. Aşağıdaki gibi bir kod parçacığımız olsun:

import React, { useState, useEffect } from "react";

const ExampleComponent = () => {
   const [name, setName] = useState("");

   useEffect(() => {
      console.log(`Name changed to ${name}`);
   }, [name]);

   return (
      <>
         <input type="text" value={name} onChange={(event) => setName(event.target.value)} />
      </>
   );
};

export default ExampleComponent;

Yukardaki kod parçacığında bir input'tan gelen değeri handle edip name state'ine aktardık. Bu işlemi canlı olarak gözlemleyebilmek ve handle ettiğimiz değerin doğruluğunu test etmek için ise useEffect hook'tan yararlandık. Örnekte input alanına girilen her bir değerden sonra useEffect hook çalıştı ve name state'inde tuttuğumuz değeri konsola yazdırdı.

useEffect hook, bağımlılık ya da bağımlılıklar alabilir. Bağımlılık alırsa, bağımlılık olarak verilen değer her güncellendiğinde useEffect tekrardan çalışır. Eğer bağımlılık boş dizi ("[]") olarak verilmişse, sayfa yalnızca ilk render edildiğinde useEffect hook 1 kere çalışır.


3) useContext Hook

React component'ları arasında veri paylaşımını sağlamak için kullanılır. Bu hook, belirli bir context'i alıp onunla ilişkilendirilmiş verilere erişim sağlar. Context, component'lar arasında paylaşılmak istenen verilerin anahtar - değer (key-value) çiftlerini içeren bir yapıdır. useContext hook sayesinde bu verilere herhangi bir ara component veya yardımcı yapı kullanmadan, prop drilling yapmadan, doğrudan erişebiliriz.

Kullanımı için öncelikle bir context oluşturarak başlıyoruz:

import { useState, createContext } from "react";

export const MyContext = createContext();

const MyProvider = ({ children }) => {
   const [count, setCount] = useState(0);

   const increase = () => {
      setCount(count + 1);
   };

   const decrease = () => {
      setCount(count - 1);
   };

   return;
   <MyContext.Provider value={{ count, increase, decrease }}>{children}</MyContext.Provider>;
};

export default MyProvider;

Bu kısımda MyContext isimli context'i createContext() metodu ile oluşturduk. State'imizi ve sayfa üzerinde işlem yapacak olan fonksiyonları tanımladık. Context üzerinde bu şekilde yapılan tanımlamalar ile tasarım ve logic kısmı birbirinden ayrılmaktadır.

Yapı incelendiğinde neredeyse normal bir component'la birebir aynı olduğu görülmektedir. Ancak bu kısımda dikkat etmemiz gereken nokta, bu context'i kullanabilmek için React'ın sarmal yapısına eklememiz gerektiğidir. Yarattığımız context'i kullanabilmek için index.js dosyasına gidip <App /> component'ini export ettiğimiz Provider ile sarıyoruz.

import React from "react";
import ReactDOM from "react-dom";
import MyProvider from "./providers/MyProvider.js";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
   <React.StrictMode>
      <MyProvider>
         <App />
      </MyProvider>
   </React.StrictMode>
);

Context'i sarmal yapıya dahil ettiğimiz yere göre etki alanı değişecektir. Bu örnekte <App /> component'ı ve altındaki tüm component'larda oluşturduğumuz context kullanılabilir.

Son olarak oluşturduğumuz context'i aşağıdaki örnekte olduğu gibi kullanabiliriz:

import React, { useContext } from "react";
import MyContext from "./MyContext";

const ChildComponent = () => {
   const { count, increase, decrease } = useContext(MyContext);

   return (
      <>
         <p>{count}</p>
         <button onClick={increase}>Artır</button>
         <button onClick={decrease}>Azalt</button>
      </>
   );
};

export default ChildComponent;

Bu kısımda ise useContext metodu ile daha önceden oluşturduğumuz MyContext'i kullanıyoruz. MyContext içinden logic işlemleri yerine getirecek olan metodları sayfa üzerinde yukardaki şekilde kullanabiliyoruz.

Bu şekilde işlemlerimizi tamamladıktan sonra MyContext içerisinde tanımladığımız fonksiyon ve state'lere, alt componentlerde de erişerek bütün data'yı proje üzerinde dağıtabiliyor ve prop drilling yapmak zorunda kalmıyoruz.


4) useReducer Hook

useState'e benzer bir şekilde state yönetimi için kullanılan bir React hook'u olan useReducer; daha karmaşık state yönetimlerini denetlerken özellikle state geçişlerinin mantıksal operatörler (if) ile yönetilmesi gerektiği durumlarda tercih edilir. Bu hook bir işlev (action) ve bir başlangıç durumu (initial state) ile birlikte kullanılır. İşlev mevcut durumu ve bir eylemi alır ve yeni bir durum döndürür.

Örnek olarak, basit bir count yönetimi düşünelim:

import React, { useReducer } from "react";

const counterReducer = (state, action) => {
   switch (action.type) {
      case "INCREMENT":
         return { count: state.count + 1 };
      case "DECREMENT":
         return { count: state.count - 1 };
      default:
         return state;
   }
};

const Counter = () => {
   const [state, dispatch] = useReducer(counterReducer, { count: 0 });

   return (
      <>
         <p>Count: {state.count}</p>
         <button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
         <button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
      </>
   );
};

export default Counter;

State yönetimi için kullanılacak olan reducer fonksiyonunu useReducer ile çağırdık ve gerekliliklerini verdik. Bu kullanımda switch-case yapısı ile state güncellenip return edilmektedir. Dispatch fonksiyonu ile de değerler sayfa üzerindeki işlemlere göre güncellenmektedir.

Böylece state güncellerken duruma bağlı bir şekilde komplike bir işlemi basite indirgeyerek useState yerine useReducer kullanıyor ve güncelleme işlemini kolaylaştırabiliyoruz.

Kullanımı useState ile benzer olsa da bir fonksiyon içerisinde birden fazla state set etmek yerine useReducer kullanarak tek bir switch yapısı içerisinde tüm durumlar handle edilebiliyor.


5) useCallback Hook

React'ta performansı optimize etmek ve gereksiz işlemleri önlemek için kullanılan bir hooktur. Özellikle foknsiyonların yeniden oluşturulmasını engellemek için kullanılır. Çünkü her fonksiyon oluşturulduğunda bunun sonucunda component yeniden render edilir. Bu durum özellikle optimize edilmemiş fonksiyonların kullanıldığı durumlarda gereksiz yere performans kaybına yol açabilir.

useCallback hook'u, bir fonksiyonu ve bağımlılık (dependency) dizisini alır ve bu işlevi yeniden oluşturmadan sadece bağımlılık dizisindeki değerler değiştiğinde güncellemeyi sağlar. Daha anlaşılır olması açısından aşağıda basit bir kullanım üzerinden açıklamaya çalışacağım.

import React, { useState, useCallback } from "react";

const Counter = () => {
   const [count, setCount] = useState(0);

   const callbackIncrement = useCallback(() => {
      setCount(count + 1);
   }, [count]);

   return (
      <>
         <p>Count: {count}</p>
         <button onClick={callbackIncrement}>Increment with useCallback</button>
      </>
   );
};

export default Counter;

Bu kısımda

const increment = () => {
   setCount(count + 1);
};

şeklinde bir fonskiyon oluşturup bu fonksiyon ile beraber artırma işlemi yapabilirdik.

Yukardaki örnekte hint kısmında gördüğümüz increment fonksiyonunu kullanmış olsaydık bu fonksiyon her render sırasında yeniden oluşturulacağından bu durum performans kaybına yol açabilirdi. Ancak callbackIncrement fonksiyonu useCallback ile oluşturulduğundan dolayı yalnızca count değeri değiştiğinde yeniden oluşturulur. Bu sayede gereksiz renderdan kaçınmış oluruz.


Bu yazıda bahsettiğimiz hook kullanımları ve daha fazlasını React'ın kendi sitesinden daha detaylı olarak inceleyebilirsiniz. Buraya tıklayarak React dokümantasyonuna ulaşabilirsiniz. Bu ve bunun gibi örnekler çoğaltılabileceği gibi daha kompleks projelerde bu hookların daha farklı kullanımlarını da görebilirsiniz.

#React #ReactHooks #LifecycleMethods #ReactComponents #useState #useEffect #useContext #useReducer #useCallback

Last updated