Gatsby’de React Hooks Kullanımı: Modern Web Geliştirmenin Gücü
Web geliştirme dünyası sürekli evriliyor ve bu evrimin en önemli duraklarından biri şüphesiz React Hooks ve Gatsby’nin birleşimidir. React Hooks, fonksiyonel bileşenlerde durum (state) ve yaşam döngüsü (lifecycle) özelliklerini kullanmamızı sağlayarak, sınıf (class) bileşenlerinin getirdiği karmaşıklığı ortadan kaldırdı. Gatsby ise React tabanlı, GraphQL destekli, performans odaklı bir statik site oluşturucu olarak modern web projelerinin vazgeçilmezi haline geldi. Bu makalede, React Hooks’un Gatsby projelerinde nasıl entegre edildiğini, veri yönetimi, performans optimizasyonu ve genel geliştirme deneyimi üzerindeki etkilerini derinlemesine inceleyeceğiz. Amacımız, bu iki güçlü teknolojinin sinerjisini anlamak ve projelerinizde en verimli şekilde kullanmanız için size kapsamlı bir rehber sunmaktır.
React Hooks’a Derin Bir Bakış
React Hooks, React 16.8 ile tanıtılan ve fonksiyonel bileşenlere durum ve diğer React özelliklerini “bağlamanın” bir yolunu sunan bir dizi fonksiyondur. Bu sayede, sınıf bileşenlerine ihtiyaç duymadan daha temiz, okunabilir ve yeniden kullanılabilir kod yazabiliriz.
useState: Temel Durum Yönetimi
useState, fonksiyonel bileşenlerde durum tutmamızı sağlayan en temel Hook’tur. Bir durum değişkeni ve bu değişkeni güncelleyen bir fonksiyon döndürür.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
Sayı: {count}
);
}
Bu örnekte, count adında bir durum değişkeni ve onu güncelleyen setCount fonksiyonu tanımlanmıştır. useState(0) başlangıç değerini 0 olarak ayarlar.
useEffect: Yan Etkilerin Yönetimi
useEffect, veri çekme, DOM manipülasyonu, olay dinleyicileri ayarlama gibi “yan etkileri” (side effects) fonksiyonel bileşenlerde yönetmek için kullanılır. Bileşenin her render’ından sonra çalışır, ancak bağımlılık dizisi (dependency array) ile bu davranışı kontrol edebiliriz.
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(https://api.example.com/users/${userId})
.then(response => response.json())
.then(data => {
setUser(data);
setLoading(false);
});
// Temizleme fonksiyonu (componentWillUnmount gibi)
return () => {
// Örneğin, bir aboneliği iptal etme
};
}, [userId]); // userId değiştiğinde yeniden çalışır
if (loading) return Yükleniyor...
;
if (!user) return Kullanıcı bulunamadı.
;
return (
{user.name}
Email: {user.email}
);
}
Bağımlılık dizisi boş bırakılırsa ([]), useEffect sadece bileşenin ilk render’ında çalışır (componentDidMount gibi). Hiç verilmezse, her render’da çalışır.
useContext: Global Durum Yönetimi
useContext, React’in Context API’sini kullanarak, prop drilling’den kaçınarak bileşen ağacında derinleşimli verilere erişim sağlar. Tema ayarları, kullanıcı kimlik doğrulama bilgileri gibi global durumlar için idealdir.
import React, { useContext, useState, createContext } from 'react';
const ThemeContext = createContext(null);
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
return (
{children}
);
}
function ThemeToggler() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
);
}
function App() {
return (
);
}
useReducer: Karmaşık Durum Mantığı İçin
useState‘in daha karmaşık bir alternatifi olan useReducer, özellikle bir durumun birden fazla alt değere sahip olduğu veya bir sonraki durumun önceki duruma bağlı olduğu durumlarda tercih edilir. Redux benzeri bir desen sunar.
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return initialState;
default:
throw new Error();
}
}
function CounterWithReducer() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Sayı: {state.count}
>
);
}
useCallback ve useMemo: Performans Optimizasyonları
Bu Hook’lar, gereksiz yeniden render’ları önleyerek performansı artırmak için kullanılır.
* useCallback: Bir fonksiyonu önbelleğe alır ve bağımlılıkları değişmediği sürece aynı fonksiyon referansını döndürür. Bu, özellikle prop olarak alt bileşenlere geçirilen callback’ler için önemlidir.
* useMemo: Hesaplamalı bir değeri önbelleğe alır ve bağımlılıkları değişmediği sürece aynı değeri döndürür. Yoğun hesaplamalar içeren durumlarda faydalıdır.
import React, { useState, useMemo, useCallback } from 'react';
function ExpensiveCalculation({ num }) {
const calculatedValue = useMemo(() => {
console.log('Hesaplama yapılıyor...');
let result = 0;
for (let i = 0; i < 100000000; i++) {
result += num;
}
return result;
}, [num]); // num değiştiğinde yeniden hesapla
return Hesaplanmış Değer: {calculatedValue}
;
}
function ParentComponent() {
const [count, setCount] = useState(0);
const [anotherCount, setAnotherCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Butona tıklandı!');
setCount(prev => prev + 1);
}, []); // Bağımlılık yok, bu yüzden handleClick referansı hiç değişmez.
return (
Count: {count}
Another Count: {anotherCount}
);
}
useRef: DOM Elemanlarına veya Render’lar Arası Değişmeyen Değerlere Referans
useRef, bir DOM elemanına doğrudan erişmek veya bileşenin render’ları arasında değişmeyen bir değeri tutmak için kullanılır. Değeri güncellendiğinde bileşenin yeniden render olmasına neden olmaz.
import React, { useRef } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// current ile input elemanına erişilebilir
inputEl.current.focus();
};
return (
<>
>
);
}
Custom Hooks: Tekrar Kullanılabilir Mantık Oluşturma
Custom Hooks, birden fazla bileşen arasında durumlu mantığı yeniden kullanmamızı sağlayan fonksiyonlardır. use ile başlayan herhangi bir JavaScript fonksiyonu bir Custom Hook olabilir. Bu, DRY (Don’t Repeat Yourself) prensibini uygulamak için harika bir yoldur.
// useWindowSize.js
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handleResize);
handleResize(); // İlk yüklemede boyutu ayarla
return () => window.removeEventListener('resize', handleResize);
}, []); // Sadece ilk yüklemede çalışır
return windowSize;
}
export default useWindowSize;
// MyComponent.js
import React from 'react';
import useWindowSize from './useWindowSize';
function MyComponent() {
const { width, height } = useWindowSize();
return (
Pencere Boyutu: {width}x{height}
);
}
Gatsby’nin Temelleri ve React İlişkisi
Gatsby, modern web geliştirmenin en popüler araçlarından biridir. React, GraphQL ve Webpack gibi teknolojileri bir araya getirerek hızlı, güvenli ve ölçeklenebilir statik siteler oluşturmanızı sağlar.
Gatsby Nasıl Çalışır? Build Time vs. Runtime
Gatsby, “build time” sırasında tüm sayfaları ve verileri önceden oluşturur. Bu, kullanıcının tarayıcısına sadece statik HTML, CSS ve JavaScript dosyalarının gönderilmesi anlamına gelir. Bu yaklaşım, sayfa yükleme sürelerini önemli ölçüde azaltır ve SEO performansını artırır. Site tarayıcıda yüklendikten sonra, React uygulaması “hydrate” olur ve dinamik etkileşimler için devreye girer. Hooks, bu “runtime” aşamasında React bileşenlerinin dinamik davranışlarını yönetmek için kullanılır.
Gatsby’de React Bileşenleri
Gatsby’de temel olarak iki tür React bileşeni vardır:
* Sayfa Bileşenleri (src/pages): Bu dizindeki her JavaScript dosyası, otomatik olarak bir web sayfası haline gelir. Örneğin, src/pages/about.js, /about URL’sine karşılık gelir.
* Bileşenler (src/components): Bunlar, sayfa bileşenleri içinde veya başka bileşenler içinde yeniden kullanılabilen genel React bileşenleridir (Header, Footer, Card vb.).
GraphQL ile Veri Katmanı
Gatsby’nin en güçlü yanlarından biri, dahili GraphQL veri katmanıdır. Build time sırasında, Gatsby sitenizdeki tüm verileri (Markdown dosyaları, API’ler, CMS’ler, resimler vb.) bir GraphQL şemasına dönüştürür. Bu, bileşenlerinizden bu verilere tutarlı ve tip güvenli bir şekilde erişmenizi sağlar.
Gatsby’de Veri Yönetimi ve Hooks
Gatsby’nin GraphQL veri katmanı ile React Hooks’u birleştirmek, veri çekme ve yönetme sürecini son derece modern ve verimli hale getirir.
useStaticQuery: Build Time’da Veri Çekme
useStaticQuery Hook’u, Gatsby 2.1’den itibaren sunulan ve sayfa dışı bileşenlerde (yani src/pages dizininde olmayan bileşenlerde) build time sırasında veri çekmek için kullanılan güçlü bir araçtır. Daha önce bu tür bileşenlerde veri çekmek için StaticQuery bileşenini kullanmak gerekiyordu, bu da render prop deseni nedeniyle daha fazla sarmalayıcı bileşen anlamına geliyordu. useStaticQuery bu süreci çok daha sadeleştirir.
import React from 'react';
import { useStaticQuery, graphql } from 'gatsby';
function SiteInfo() {
const data = useStaticQuery(graphql
query SiteTitleQuery {
site {
siteMetadata {
title
description
}
}
}
);
return (
{data.site.siteMetadata.description}
);
}
Önemli Notlar:
* useStaticQuery Hook’u sadece bir kez kullanılabilir ve dinamik sorguları desteklemez. Yani sorgu içinde değişken kullanamazsınız.
* Sorgu, build time’da çalışır ve sonuçları bileşeninize statik olarak enjekte eder.
Sayfa Sorguları (Page Queries)
Sayfa bileşenlerinde (src/pages altındaki dosyalar), useStaticQuery‘den farklı olarak, doğrudan bileşenin altında export const query = graphql yapısını kullanarak GraphQL sorguları tanımlayabiliriz. Bu sorguların sonuçları bileşene data prop’u olarak geçer. Bu sorgular, dinamik değişkenler (örneğin, bir blog yazısının slug’ı) alabilir.
// src/pages/blog/{GatsbyMdx.slug}.js (Dinamik sayfa oluşturma örneği)
import React from 'react';
import { graphql } from 'gatsby';
function BlogPost({ data }) {
const post = data.mdx;
return (
{post.frontmatter.date}
{/ Mdx içeriği /}
);
}
export const query = graphql
query BlogPostBySlug($id: String!) {
mdx(id: { eq: $id }) {
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
}
body
}
}
;
export default BlogPost;
Bu örnekte, $id değişkeni Gatsby’nin gatsby-node.js dosyasında createPages API’si ile dinamik olarak sağlanır.
Runtime’da Veri Çekme: useEffect ile Client-side Rendering (CSR)
Gatsby genellikle build time’da veri çekse de, bazı durumlarda (örneğin, kullanıcıya özel içerik, dinamik yorumlar, gerçek zamanlı veriler) “client-side” (tarayıcı tarafında) veri çekmek gerekebilir. İşte burada useEffect devreye girer.
import React, { useState, useEffect } from 'react';
function CommentsSection({ postId }) {
const [comments, setComments] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(https://api.example.com/posts/${postId}/comments)
.then(res => res.json())
.then(data => {
setComments(data);
setLoading(false);
})
.catch(error => {
console.error("Yorumlar yüklenirken hata oluştu:", error);
setLoading(false);
});
}, [postId]); // postId değiştiğinde yorumları yeniden yükle
if (loading) return Yorumlar yükleniyor...
;
if (comments.length === 0) return Henüz yorum yok.
;
return (
Yorumlar
{comments.map(comment => (
{comment.author}
{comment.text}
))}
);
}
Bu yaklaşım, Gatsby’nin statik yapısını korurken, gerektiğinde dinamik içerik eklemenizi sağlar.
Gatsby ve Performans Optimizasyonunda Hooks’un Rolü
Gatsby zaten performans konusunda oldukça başarılıdır, ancak React Hooks ile bu performansı daha da ileriye taşıyabiliriz.
useMemo ve useCallback ile Gereksiz Render’ları Engelleme
Daha önce de bahsettiğimiz gibi, useMemo ve useCallback Hook’ları, özellikle karmaşık ve sıkça render olan bileşenlerde performans darboğazlarını önlemek için kritik öneme sahiptir. Gatsby projelerinde, bileşen hiyerarşileri büyüdükçe bu Hook’ların kullanımı daha da belirginleşir. Özellikle listeler veya animasyonlar gibi yoğun render döngülerinin olduğu yerlerde gereksiz hesaplamaları veya fonksiyon referanslarının yeniden oluşturulmasını engellerler.
Lazy Loading ve React.lazy, Suspense
Gatsby, varsayılan olarak kod bölme (code splitting) yapar, ancak React.lazy ve Suspense ile daha granüler bir kontrol sağlayabiliriz. Bu, yalnızca belirli bir bileşen ekranda göründüğünde veya belirli bir etkileşim gerçekleştiğinde yüklenmesini istediğimiz bileşenler için kullanışlıdır.
import React, { Suspense } from 'react';
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function MyPage() {
const [showLazy, setShowLazy] = useState(false);
return (
{showLazy && (
Yükleniyor... }>
)}