




Merhaba arkadaşlar bugün sizlerle beraber react kullanarak uygulamamızda optimizasyonu nasıl sağlarsınız onu inceleyeceğiz.
Genel react bilginizin olduğunu düşünerek bu içeriği oluşturduğumu bildirmek isterim.
Öncelikle uygulama hakkında kaynak kodlarımız üzerinden konuşalım.Daha hızlı bir şekilde konumuza odaklanmamız için sizlere bazı kodlar vereceğim bu kodları kopyala yapıştr yaparak .
Ben NextJS üzerinden bu işlemi gerçekleştireceğim siz isterseniz pureReact isterseniz Vite üzerinden işlemlerinizi gerçekleştirebilirsiniz.
https://github.com/kubilaybzk/react-memorization
Reposu üzerinden kodları takip edebilirsiniz bu yazıda değindiğim her konu için farklı farklı commitler attım.
Page.js dosyamız
https://medium.com/media/2dd20fb8081475916d77599cbd31a075/hrefBlogForm.js componentimiz.
https://medium.com/media/dc30c7db77118345d5443c863b33f9c1/hrefBlogItem.js componentimiz.
https://medium.com/media/ba28ea9bb45d061b92bc761c3fb19e70/hrefPostList.js componentimiz.
https://medium.com/media/be45bfe0597dd9897a2eaafa766cf56e/hrefHeader.js componentimiz.
https://gist.github.com/kubilaybzk/216741d772adb12b9eb8043f67ecb35f#file-header-js
Tüm bunları yaptıktan sonra uygulamamızda dosyalarımız ekteki gibi olmalı.
Şimdi tüm bu düzenlemeleri yaptıktan sonra , npm run dev diyerek uygulamamızı başlatalım ve incelemeye başlayalım.
İlk olarak , geliştirici konsol üzerinden hangi alanlar uygulamamıza ilk request atıldığında kullanıcının tarayıcısında render ediliyor ve hangi alanlar uygulamada bulunan herhangi bir state değişiminde render ediliyor inceleyelim.
Şimdi uygulamamız ilk defa tarayıcıya yüklendiği zaman ,
Gördüğünüz gibi ilk olarak bütün componentler render edildi.
Bu gayet normal bir durum değil mi ? Fakat BlogItem render edilmedi çünkü uygulamamız ilk başladığında BlogItem içine gönderdiğimiz state boş. React burada ekstra bir render işlemi gerçekleştirmiyor.
Şimdi uygulamamızda bulunan From içine bir kaç değer girelim .
Gördüğünüz gibi hala herhangi bir render işlemi yok.
Bunun sebebi input değerlerini usestate kullanmak yerine ref olarak almamız, eğer input alanında onChange fonksiyonu içinde her bir değişim için state güncellemesi yapmış olsaydık burada her tuşa bastığımızda input alanı yeniden render olacaktı.
Şimdi butonumuza tıklayalım ve ne olacak görelim.
Gördüğünüz gibi tek bir state güncellememize rağmen , Header, Form ,HomePage tekrardan render edildi.
Peki bir alan daha ekleyelim örnek olarak 2 yazalım inputların içine ;
Gördüğünüz gibi burada ise şöyle bir durum söz konusu oldu ,
2 PostList içine eklendi evet ama yinede Header , Form , HomePage alanları yeniden render edildi ek olarak daha önce eklemiş olduğumuz ve Liste alanında hali hazırda olan ve içinde 1 yazan alanda render edildi.
İşte burada çoook büyük bir liste olduğunu düşünün mesela 1'de başlayarak son değer 1.000.000 olacak şekilde veri eklemiş olalım bu listeye her bir eklemede bu alanların tekrar tekrar render olması performans açısından ne kadar dezavantajlı bir yöntem olacak düşünebiliyor musunuz?
İşte React bu durumlarda kullanmamız için bizlere Memo,UseMemo ve UseCallBack olacak 3 adet performans optimizasyonu için kullanabileceğimiz Hooklar sağlamakta.
Şimdi ilk olarak Memo ile başlayalım.
React Memo
React'te bir bileşenin performansını artırmak amacıyla kullanılan bir yüksek seviye bileşen (higher-order component) veya yüksek seviye fonksiyon (higher-order function) olarak bilinir. Bu işlev, bir bileşeni hatırlayarak ve gereksiz yeniden render işlemlerini önleyerek performansı artırır.
Normalde, bir bileşen herhangi bir değişiklik olduğunda tekrar render edilir. Ancak bazen, bir bileşenin prop’ları veya durumu değişmese bile yeniden render edilmesi gerekebilir. Bu, özellikle büyük ve karmaşık bileşenlerde performans sorunlarına neden olabilir.
Mesela bir Component kendisi render edildiğinde içinde bulunan child component’in state’i değişmemesi durumunda bile kendisi yeniden render edildiği için child componentleri render edilebilir.
React.memo ile bir bileşen sadece belirli koşullar altında yeniden render edilir. İlgili bileşenin prop'ları veya durumu değiştiğinde sadece o zaman yeniden render edilir. Bu, gereksiz yeniden render işlemlerini önleyerek uygulamanın daha verimli çalışmasını sağlar.
Örnek kullanımı şu şekildedir:
Biz yukarıda listemize 1 ve 2 değerlerini eklemiş olduğumuz iki farklı durum için konsol üzerinde hangi component’in render edildiğini console.log(…) sayesinde görüyoruz , burada her iki durum içinde Header alanının render edildiğini fark ettiniz mi ?
Peki,Header alanında herhangi bir değişiklik söz konusu olmadığı halde neden durmadan render işlemi gerçekleşiyor.
Bunun sebebi aslında çok basit , Page.js dosyamızı incelediğimizde burada bizim bir state değerimiz olduğunu görüyoruz.Bu state her değiştiğinde react bu state’in içinde bulunan her bir component için tekrar bir render işlemi gerçekleştiriyor çünkü parent olan component render ediliyor.
İşte burada Header’ın tekrar tekrar render edilmesi aslında istenmeyen ve performans açısından pek doğru olmayan bir yöntem.
Burada her state değişiminde header alanının tekrar tekrar render olmasını engellemek için burada biz MEMO kullanacağız.
Kullanımı oldukça basit. Memo ile memorize etmek istediğimiz hedef componentimizi parametre olarak ayarlıyoruz.
Şimdi uygulamamızı resfreh edelim ve bir adet listeye eleman ekleme işlemi gerçekleştirelim.
Görüldüğü gibi state değişmesine rağmen header tekrar render edilmedi bu sayede uygulamamızda bir performans artışı yaşamış olduk.
Ek olarak bu memo yapısını uygulamamızda PostList.js içinde kullanabiliriz.Burada nasıl bir problemimiz vardı Liste içine eklenen her bir eleman için React daha önce eklenmiş olan bütün elemanlarıda tekrar tekrar render ediyordu.
Görsel üzerinden hatırlamamız gerekirse;
Şimdi burada biz her bir elemanı farklı bir component olacak şekilde ayarlamıştık yani PostList tüm liste , BlogItem ise liste içinde bulunan tek bir component.
Burada listeyi memo içine sarmalamak şuan istediğimiz durum için bizim hiçbir işimize yaramayacak (sırayla gidiyoruz) , biz liste içinde bulunan elemanları memo kullanarak Memorize etmemiz bizim için istediğimiz optimizasyonu sağlayacak.
Daha anlaşılır bir şekilde açıklamak istiyorum durumu liste içinde 2 farklı Post eklemiş olalım. 3. Post eklendiği anda konsol üzerinde neler oluyor inceleyelim.
Render olan her bir component burada gözülüyor peki title:1 ve title:2 olan değerler neden render ediliyor ? Bunu istemiyoruz hemen memo kullanarak bu problemi çözelim.
Burada başka bir memo kullanım şeklini ele aldık fonksiyon’un tamamını verip kod karmaşası yaratacağımızı export işleminde memo tanımlaması yaptık.
Şimdi 4. post durumunu ekleyelim ve neler olup bitiyor bir göz atalım.
Gördüğünüz gibi sadece o an eklenen eleman render edildi daha önce eklenmiş olan 1,2 ve 3 post componentleri render edilmedi.
useCallback nedir ?
useCallback, React'te bir fonksiyonun hatırlanmasını (memoization) sağlayan bir hook'tur. Bu hook, belirli bağlamlarda kullanılan fonksiyonların gereksiz yere her render sırasında yeniden oluşturulmasını önlemek için kullanılır. Bu, performansı artırabilir çünkü her render sırasında yeni bir fonksiyon oluşturmak yerine aynı fonksiyonun referansını kullanır.
Genellikle, useCallback ile oluşturulan fonksiyonlar, başka bileşenlere prop olarak geçirildiğinde veya bağlamalar (context) içinde kullanıldığında faydalıdır. Bu, bu fonksiyonların gereksiz yere her render sırasında yeniden oluşturulmasını önler ve böylece gereksiz işlemci kaynaklarının tüketilmesini engeller.
useCallback işlevi iki argüman alır:
- Birincisi, hatırlanmasını istediğiniz fonksiyonun kendisidir.
- İkincisi, bağlamasını (dependency array) oluşturmak istediğiniz değişkenlerin bir dizisidir. Eğer bu değişkenler değişirse, hatırlanan fonksiyon yeniden oluşturulur. Eğer bu diziyi boş bırakırsanız, fonksiyon yalnızca bileşenin ilk renderında oluşturulur ve daha sonra herhangi bir değişiklikte yeniden oluşturulmaz.
Basit bir açıklama gerekirse parent component içinde tanımlanan bir fonksiyonu child component’e yolluyorsanız, parent her render edildiğinde child render edilmesin istiyorsanız kullanmanız gereken Hook.
İhtiyaçları, Fonksiyon ve dependency array.
Şimdi teknik bilgileri verdikten hemen sonra,
Uygulamamızda ne olduğunu anlamak için ufak bir modefiye işlemi gerçekleştirelim.
BlogForm içinde bulunan onSavePostClicked fonksiyonunu app.js içine taşıyalım.Daha sonra props olarak bu fonksiyonu BlogForm componentine gönderelim.Daha sonra yeni bir state tanımlayalım herkesin bildiği basit bir count state’i olsun bu. App.js dosyamız ;
https://medium.com/media/9c20f6220d6a8ce79dc7e245e9a043a2/hrefYukarıda buton’a her tıkladığımızda BlogFrom’un render edildiğini görüyorsunuz.
Şimdi BlogFrom’u memo hookunu kullanarak export edelim.
Tekrar uygulamamızda counter değeriyle oynayalım ve sonuca bir göz atalım,
Gördüğünüz gibi memo kullanmamıza rağmen burada Form durmadan render ediliyor bunun sebebi memory içinde bu fonksiyonun tutulmaması her render edildiğinde bu fonksiyonun yeniden ve yeniden memory içinde oluşması.
İşte burada bu durumu engellemek için biz props olarak geçtiğimiz fonksiyonları useCallBack içinde kullanırsak burada ilgili fonksiyonu memory içinde tutmuş oluruz bu sayede BlogForm alanının durmadan render edilmesini engellemiş oluruz.
Gördüğünüz gibi her butona tıkladığımızda BlogForm tekrar tekrar render ediliyor işte burada usememo işe yaramıyor aslında işe yarıyor tek sıkıntı Parent component olan page.js’in içinde olan fonksiyon her çağrıldığında fonksiyonun memory üzerinde değişimleri oluyor.
İşte bu gibi durumlarda usecallback kullanmamız gerekiyor.Tek yapmamız gereken UseCallBack Hook’unun içine bizim kullandığımız onSavePostClicked fonksiyonunun parametre olarak verilmesi.
Dependency Array kısmında ise hangi değer değişirse bunun çalışmasını istiyoruz bunu bildirmemiz gerekiyor.
const onSavePostClicked = useCallback((title, content) => {
setBlogList((prevBlogList) => [
...prevBlogList,
{
title,
content,
},
]);
}, []);
Şimdilik dependency kısmını boş bırakıyoruz artık sadece sayfa yüklendiğinden 1 kere çalışacak eğer biz blogList state’i her değiştiğinde bu fonksiyonun çalışmasını istersek burada dep. array kısmına bloglist state’ini
const onSavePostClicked = useCallback((title, content) => {
setBlogList((prevBlogList) => [
...prevBlogList,
{
title,
content,
},
]);
}, [BlogList]);
Burada başka bir örnek daha vermek istiyorum çünkü burada mantığını anlatmak biraz karışık ve anlaşılması başlangıç aşamasında biraz zormuş gibi geldi bana .
Basit bir counter uygulaması gerçekleştirelim.Ben uygulamada yeni bir route oluşturarak example 2 isminde başka bir app.js dosyası içinde aşağıda bulunan kodları oluşturdum. Öncelikle kodları sizinle paylaşayım,
"use client";
import React, { memo, useCallback, useState } from "react";
export default function CallBackExample() {
console.log("App Render");
const [count, setCount] = useState(0);
const [texy, setText] = useState();
//Arttırma fonksiyonumuz
const increment = () => {
setCount(count + 1);
};
return (
<div className="w-full h-full flex gap-4 justify-center items-center align-middle flex-col">
<div>{count}</div>
<Counter increment={increment} />
<input className="border" type="Text" onChange={(e) => setText(e.target.value)} />
</div>
);
}
const Counter = memo(({ increment }) => {
console.log("Counter Render");
return (
<>
<button onClick={() => increment()}>+</button>
</>
);
});
Uygulamamız çok basit bir counter uygulaması şimdi burada biz useMemo ile counter componentini memory içinde saklayacak şekile getirdik.
Ama burada içeriye biz increment fonksiyonunu yolladık burada beklentimizin konsol üzerinden Counter Rendered değerini görmemek olmaması gerekiyor burada biz bir fonksiyon çağırıyoruz bundan dolayı render oluyor ve her render işleminde bu fonksiyonun memory üzerinde tutulduğu yer değişiyor. useCallback kullanarak fonskiyonumuzu yeniden design edelim ve dependency kısmını boş bıraklım.
"use client";
import React, { memo, useCallback, useState } from "react";
export default function CallBackExample() {
console.log("App Render");
const [count, setCount] = useState(0);
const [texy, setText] = useState();
//Arttırma fonksiyonumuz
const increment = useCallback(() => {
setCount(count + 1);
}, []);
return (
<div className="w-full h-full flex gap-4 justify-center items-center align-middle flex-col">
<div>{count}</div>
<Counter increment={increment} />
<input
className="border"
type="Text"
onChange={(e) => setText(e.target.value)}
/>
</div>
);
}
const Counter = memo(({ increment }) => {
console.log("Counter Render");
return (
<>
<button onClick={(e) => increment()}>+</button>
</>
);
});
Şimdi konsol üzerinde uygulamamızı inceleyelim ve neler olduğunu inceleyelim.
Gördüğünüz gibi ne kadar çok tıklarsam tıklayayım tek bir kere çalışıyor bunun sebebi react’in bu fonksiyonu ne zaman çalıştırması gerek bilmiyor olması, burada dependency kısmına biz counter değerini verirsek bu değer her değiştiğinde bunu çalıştırması gerektiğini bildirmiş oluruz.
const increment = useCallback(() => {
setCount(count + 1);
}, [count]);
Artık butona her bastığımızda counter değeri artacak ve bu fonksiyon state her değiştiğinde yeniden ve yeniden memory içinde atanacak.
UseMemo nedir ?
useMemo, React'te hesaplama işlemleri sonucunda oluşturulan değerleri hatırlamanıza (memoization) ve gereksiz hesaplamaların önüne geçmenize yardımcı olan bir hook'tur. Bu hook, belirli değerlerin hesaplamalarının gereksiz yere her render sırasında tekrar tekrar yapılmamasını sağlar, bu da uygulamanızın performansını artırabilir.
useMemo işlevi iki argüman alır:
- İlk argüman, hesaplamayı yapacak olan fonksiyondur.
- İkinci argüman ise bu hesaplamanın ne zaman yeniden hesaplanması gerektiğini belirleyen bir bağlama dizisidir (dependency array). Eğer bağlama dizisi içindeki değişkenler değişirse, hesaplama işlemi tekrar yapılır. Eğer bağlama dizisi boşsa, hesaplama yalnızca bileşenin ilk render’ında yapılır ve daha sonra herhangi bir değişiklikte tekrarlanmaz.
Yani basit bir şekilde anlatmak gerekirse useMemo ile fonksiyonlardan dönen değerler memoized edilir. Genellikle expensive dediğimiz durumlarda kullanılır.
Bu konu hakkında harika bir örnek paylaşan Berkant Kaya’nın örneğinden faydalanalım.
App klasörü içinde bir adet klasör oluşturalım ve app.js içine ekteki kodu yazalım.
"use client"
import React, { useState } from 'react'
export default function page() {
//Bir adet count verilerini tutan state olsun.
const [count, setCount] = useState(0);
const [text, setText] = useState("");
//Memory üzerinde çok fazla işlem yapan bir fonksiyon
const largeFunction=()=>{
[...new Array(100000000)].forEach((item)=>{})
return count*3
}
const result =largeFunction();
return (
<>
<div>{count}</div>
<button onClick={()=>setCount(count+1)}>
Arttır
</button>
{result}
<input className='border' onChange={(e)=>setText(e.target.value)}/>
</>
)
}
Bu sayfayı ilk açıldığı zamana ne kadar yavaş yükleniyor yenile butonundan görebiliyorsunuz değil mi :)
şimdi burada söyle bir işlem yapacağız bu fonksiyonun bir kere sayfa yüklenirken yada count sayısı değiştiği zaman çalışmasını sağlayacağız çalışma sonucunda elde edilen veri (sonuç) ondan sonra yapılan hiçbir işlemde bu fonksiyon tekrar çalışmayacak.
Senaryamuz şu şekilde olacak butona bastığımız zaman bu fonksiyon yeniden hesaplanacak ve elde edilen veriyi memorize edecek.
Onun dışında sayfada bulunan state değerlerinden etkilenmeyecek bu sayede yeniden hesaplama yapmayacak.
Bunun için UseMemo kullanacağız yapı olarak usecallback ile tamamen aynı yapıya sahip bu hook.
const largeFunction=useMemo(()=>{
[...new Array(100000000)].forEach((item)=>{})
return count*3
},[count])
yapılan işlem bu kadar basit aslında şimdi 2 durum içinde bu geliştirdiğimiz component içinde neler oluyor inceleyelim.
İlk durum sadece input alanı içine bilgiler girelim ve text state’inde manipülasyon uygulayalım.
Şimdi bir diğer durum olarak arttır butonuna basalım.
Gördüğünüz gibi ne kadar uzun sürüyor tekrar hesaplama işlemi .
Şimdi kısa bir özet geçmek gerekirse;
useCallback hooku → geriye memoized bir fonksiyon ddöndürür.
useMemo hooku → useCallback ile çok benzer olup tek farkı geriye memoized bir fonksiyon değil o fonksiyonun döndürdüğü değerleri döndürür.
Memo hooku → İlgili bileşenin prop’ları veya durumu değiştiğinde sadece o zaman yeniden render edilmesini sağlar.
Virtualization
React Virtualization’in temel amacı, büyük veri listelerini sayfada görünür öğelerle sınırlamak ve yalnızca kullanıcının görüntülediği kısmı renderlemektir. Bu, performans artışı , daha az bellek kullanımı ve daha hızlı yanıt gibi avantajlara sebep olur. Yöntemin çalışma mantığını şöyle açıklayayım , doom üzerinde 100000 satır bir tablo oluşturduğunuzu ya da 100000 satır ürün listelediğinizi gerektiğini düşünün .
Sayfa ne kadar yavaş yüklenir ve kullanıcının tarayıcısı üzerinde yaşanacak olan donmaları ve yavaşlamaları düşünebiliyor musunuz?
Daha akılda kalıcı bir örnek vermemiz gerekirse , twitter üzerinden örnek verebiliriz bu konuyu twitter’da ana sayfadan aşağıya doğru inmeye başlayınca ne kadar fazla tweet görüntülersek görüntüleyelim hiçbir donma kasma yaşamıyoruz bunun sebebini söyle inceleyelim.
Geliştirici konsol üzerinden tweetlerin render edildiği html kodları incelendiğinde ne olursa olsun 5 tane verinin bize gösterildiği geri kalan verilerin ekranda gözükmediğini fark ettiniz mi .
İşte burada bahsettiğimiz teknoloji bu işe yarıyor.
Peki bu teknoloji nerelerde kullanılır ?
- Listeler için Infinite Scrolling: Liste benzeri bileşenlerde sonsuz kaydırma (infinite scrolling) kullanarak, sayfa içinde daha fazla öğeyi dinamik olarak yükler. Bu, kullanıcılar listenin sonuna yaklaştıkça daha fazla verinin yüklenmesini sağlar.
- Windowing (Pencereleme): Büyük veri listelerini bir pencere içinde sınırlamak, yalnızca pencere içindeki öğeleri görüntülemeyi ve diğerlerini gizlemeyi içerir. Bu, büyük veri listelerini etkili bir şekilde işlemenin bir yoludur.
- Virtualized List veya Grid Componentleri: React Virtualization için özel olarak tasarlanmış bileşenler, büyük veri listelerini veya tabloları verimli bir şekilde işlemek için kullanılabilir.
Ufak bir virtualiz kütüphanesi kullanarak bir örnek üzerinde inceleyelim.
npm install react-virtualized
import React from 'react';
import { List } from 'react-virtualized';
// Büyük bir liste verisi oluşturalım
const data = Array.from({ length: 1000 }, (_, index) => `Öğe ${index + 1}`);
function VirtualizedList() {
return (
<div>
<h1>Büyük Liste</h1>
<List
width={300} // Liste genişliği
height={400} // Liste yüksekliği
rowCount={data.length} // Öğe sayısı
rowHeight={30} // Her öğenin yüksekliği
rowRenderer={({ index, key, style }) => (
<div key={key} style={style}>
{data[index]}
</div>
)}
/>
</div>
);
}
export default VirtualizedList;
Bu örnek, 1000 öğeyi içeren bir büyük liste görselleştirir. List componenti, react-virtualized kütüphanesinin bir parçasıdır ve yalnızca görünen öğeleri renderler. rowRenderer Ekranda oluşacak olan component için gerekli olan düzenlemeleri alır.
Elimden geldiğince performans optimizasyonu ve hooklar üzerinden konuyu anlatmaya çalıştım umarım anlaşılır ve güzel bir yazı olmuştur.
Kaynaklar :
https://medium.com/media/912a57b8d8d57ee943acfa38a1f14dbd/hrefhttps://medium.com/media/9acb83e982a9bf225d2585d56c1c82a4/hrefhttps://medium.com/media/3487628edaf8fcb57caaf7c6a3581545/hrefhttps://medium.com/media/8b014714ce7e37a3c6af0935884cb727/hrefhttps://medium.com/media/e04d0510341555c1a8bd59317534bace/hrefhttps://medium.com/media/63e3c6d6dee87783999f659535e25809/hrefhttps://medium.com/media/2702a85bee5b045f6064cdd9749538c7/href
Redux Toolkit nedir ? Neden tercih etmeliyim.
Redux Nedir?
Tek bir cümle ile açıklamak gerekirse , geliştirdiğimiz projelerde global olarak stateleri yönetmemizi ve bu statelere müdahale etmemizi sağlayan bir teknoloji.
Global olarak yönetmek derken neyden kast ettiğimi bir örnek üzerinden açıklayayım öncelikle.Bildiğiniz gibi son dönemlerde uygulamalarda tema özellikleri söz konusu olmaya başladı örnek olarak twitter’ı ele alalım.
React ile geliştirilen Twitter’ın genel olarak layout kısmını inceleyelim ve konumuza uygun olacak şekilde bir layout haritası oluşturup uygulamamızı componentlere bölelim. Dark yada white mode için classname içinde bir kontrol gerçekleştirdiğimizi ve ona göre classların aktif pasif olacak şekilde uygulamanın UI kısmının render edildiğini düşünelim.
Gördüğünüz gibi sağ tarafta bir navigation menümüz orta alanda yukarıdan aşağıya doğru ; kullanıcı bilgileri içeren bir component onun altında bir tab bar mevcut. Sol tarafta ise tekrar yukarıdan aşağıya doğru takip önerileri ve gündem kısmı bulunmakta.
Şimdi burada navigation kısmının bir component, kullanıcı bilgilerinin olduğu alanı başka bir comp. tab bar kısmını başka bir component olarak düşünelim. Uygulamamızda temayı değiştiren butona tıkladığımız zaman parçalara ayırdığımız ve projemizde ayrı ayrı javascript yada typescript olarak geliştirilen bu componetlerin sahip olduğu css kodlarının o anki koşula göre değişmesi gerekiyor değil mi ?
Burada navigation menüsünde bulunan Tweet butonunun hemen altında başka bir buton olduğunu düşünelim, bu butona basınca uygulamamızda bulunan bir state üzerinde bir değişim gerçekleşecek ve biz bu sayede menümüzü dark yada white olarak değiştirecek şekilde bir geliştirme yaptığımızı düşünelim , butona bastık ve temamız dark ise white oldu (yada tam tersi).Şimdi bu değişimi yaptıktan hemen sonra bizim orta alanda bulunan kullanıcı sayfamızın temasına müdahale edebilmemiz için nasıl bir yöntem izlememiz gerekiyor ?
Hemen aklınıza props olarak göndermek geliyor değil mi ? Peki o zaman burada her bir component içinde props olarak bu tema state’i aktarılacak ise parent olan bir componente biz bu state değerini nasıl yollayacağız? State’i parent içinde tanımlarız o zaman dediğinizi duyar gibiyim peki parent içinde state tanımını gerçekleştirdik ve bunu child componentlere props olarak aktarırken ne kadar fazla maliyet ve ne kadar çok fazla karmaşa ortaya çıkacak düşüncesi bile insanın kafasını karıştıracak cinsten değil mi ? İşte bu ve buna benzer bir durumda uygulamızda global olarak bu tema değerini tutacağımız bir state olması gerekiyor ve bu state’e her bir component içinde erişerek gerekli işlemleri yapmamız gerekiyor.
Bu verdiğim örnek aslında pek tercih edilmeyen ve kullanılmayan bir yöntem günümüzde bahsettiğim tema güncelleme işlemleri için bir çok farklı yöntem bulunmakta fakat örnek olarak konunun en kolay izah edileceği örneğinde bu olduğunu düşünüyorum.
Şimdi gelelim asıl konumuza global olarak state yönetimi için neden redux-toolkit kullanmalıyım.
Redux Toolkit, Redux tabanlı uygulamalar geliştirmeyi daha kolay ve verimli hale getiren bir kütüphanedir. Redux, JavaScript uygulamalarında durum yönetimi için yaygın olarak kullanılan bir kütüphanedir. Ancak Redux, kullanımı bazen karmaşıklaşabilen çok fazla tekrarlayan kod gerektirebilir. Redux Toolkit, bu sorunları çözmek ve Redux’i daha kullanıcı dostu hale getirmek için tasarlanmıştır.
Redux Toolkit’in bazı temel özellikleri şunlardır:
- Azaltılmış Boilerplate Kod: Redux Toolkit, tipik Redux uygulamalarında yazmanız gereken tekrarlayan kod miktarını azaltır. “createSlice” ve “createAsyncThunk” gibi yardımcı işlevlerle, reducer’larınızı, eylemlerinizi ve çok sayıda işlemi daha az kodla tanımlamanıza olanak tanır.
- İçe Aktarmaların Kolaylığı: Redux Toolkit, başka dosyalardan Redux özelliklerini içe aktarmayı ve kullanmayı kolaylaştırır. Bu sayede kodunuz daha organize ve okunur hale gelir.
- Özelleştirilebilirlik: Redux Toolkit, her bir özelliği özelleştirmenize ve karmaşık kullanım senaryolarını ele almanıza olanak tanır. Bu, büyük ölçekli uygulamalarda esneklik sağlar.
- Asenkron İşlemleri Kolaylaştırma: “createAsyncThunk” ile Redux Toolkit, asenkron işlemleri yönetmeyi basitleştirir. API çağrıları ve diğer asenkron işlemler için otomatik olarak eylemler oluşturur ve hata işleme yetenekleri sunar.
Redux Toolkit’i tercih etmenin bazı avantajları şunlar olabilir:
- Daha az kod yazma: Redux Toolkit, Redux’in sunduğu özellikleri daha az kodla kullanmanızı sağlar, böylece geliştirme süreci daha hızlı hale gelir.
- Daha kolay bakım: Azaltılmış boilerplate kod, uygulamanızın bakımını daha kolay ve sürdürülebilir hale getirir.
- Asenkron işlemleri yönetme kolaylığı: Redux Toolkit, asenkron işlemleri kolayca entegre etmenizi ve yönetmenizi sağlar.
- Daha iyi organizasyon: Redux Toolkit, kodunuzu daha düzenli bir şekilde organize etmenize yardımcı olur.
Ancak Redux Toolkit’i tercih etmek, projenizin gereksinimlerine ve ekibinizin deneyimine bağlıdır. Küçük projelerde veya Redux’i daha derinlemesine anlamak istediğiniz durumlarda, doğrudan temel Redux kullanabilirsiniz. Redux Toolkit, özellikle büyük ölçekli projelerde ve daha hızlı geliştirme süreçleri gerektiren durumlarda büyük bir avantaj sağlayabilir.
NextJS ve Redux kullanarak bir proje geliştirelim.
İlk olarak bir NextJS uygulaması oluşturalım.
npx create-next-app redux-toolkit-medium
Daha sonra projemize Redux Toolkit’i kuralım.
npm install @reduxjs/toolkit react-redux
Şimdi kurulumlarımızı hallettikten hemen sonra ilk olarak yapmamız gereken bir adet store oluşturmak.
Store nedir ?
Redux store, Redux’ın uygulama durumunun yönetildiği merkezi bir depodur. Redux store, uygulama verilerini ve durumunu tek bir yerde saklayarak, bu verilere erişim ve güncelleme işlemlerini düzenler.
Şimdi uygulamızın içine bir adet Redux adında klasör ekleyelim.
Daha sonra bu klasör içine store adında bir js dosyası oluşturalım bu store dosyasının içine ekte bulunan kodları yazalım
import { configureStore } from '@reduxjs/toolkit'
export const store = configureStore({
reducer: {},
})
Redux Toolkit kullanırken configureStore fonksiyonunda tanımlanan reducer alanı, uygulamanızın durumunu yöneten kesimlerin (slices) veya özel reducer'ların birleştirildiği yerdir. Reducer'lar, eylemleri işleyen ve uygulama durumunu güncelleyen işlevlerdir.
Şimdi burada anlattıklarım biraz teknik oldu farkındayım slice hakkında hiçbir bilgi vermeden slice lar burada tanımlanır gibi bir cümle kurdum .
Şimdi slice nedir ona bakalım .
Slice nedir ?
Redux Toolkit’de “slice,” bir uygulamanın durumunun yönetildiği ve bu durumun bir parçasını temsil eden bir kavramdır. Sliceler, reducer fonksiyonları ve bu slicelerin başlangıç durumları (initial state) dahil olmak üzere birçok özelliği içerir. Slice’lar, Redux uygulamalarını daha düzenli ve modüler hale getirmek için tasarlanmıştır.
Bir Redux Toolkit slice’inin temel özellikleri şunlardır:
name: Slice'ın adıdır. Bu, store içindeki slice'ı tanımlamak için kullanılır ve genellikle eylemler ve reducer'lar arasında iletişim kurmak için kullanılır.
reducer: Slice'ın ana reducer fonksiyonunu tanımlar. Bu reducer, slice'ın başlangıç durumunu ve eylemleri işler.
initialState: Slice'ın başlangıç durumunu temsil eder. Bu, slice'ın reducer'ı tarafından kullanılır ve uygulama başladığında slice'ın ilk durumunu tanımlar.
Şimdi Redux klasörünün hemen altına features adında başka bir klasör oluşturalım.Şimdi daha sonra counter adında başka bir klasör oluşturup bunun içine counterSlice adında bir js dosyası oluşturalım. Bu dosyanın içinde slice tanımını gerçekleştirelim.
import { createSlice } from '@reduxjs/toolkit'
const initialState = {
value: 0,
}
export const counterSlice = createSlice({
/*
Name =>
Bu, store içindeki slice'ı tanımlamak için kullanılır ve
genellikle eylemler ve reducer'lar arasında iletişim kurmak için kullanılır.
Reducer =>
Slice'ın ana reducer fonksiyonunu tanımlar.
Bu reducer, slice'ın başlangıç durumunu ve eylemleri işler.
initialState =>
Slice'ın başlangıç durumunu temsil eder.
Bu, slice'ın reducer'ı tarafından kullanılır ve uygulama başladığında slice'ın ilk durumunu tanımlar.
*/
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
Şimdi tüm uygulamızı burada kapsayacak şekilde redux’ı kullanarak projemize entegre etmemiz lazım evet evet Provider kullanacağız dediğinizi duydum :)
Daha önce oluşturduğumuz Redux klasörünün hemen altına bir adet ReduxProvider adıda yeni bir javascript dosyası oluşturalım ve provider’ı burada oluşturalım.
"use client";
import React from "react";
import { store } from "./store"; //Kendi oluşturduğumuz store.
import { Provider } from "react-redux";
export default function ReduxProvider({ children }) {
return (
<Provider store={store}>
{children}
</Provider>
);}
Şimdi app dosyasının hemen altında bulunan layouth.js içine oluşturduğumuz bu provider’ı kullanarak sarmalama işlemini gerçekleştirelim.
import ReduxProvider from "./Redux/ReduxProvider";
import "./globals.css";
//....
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<ReduxProvider>{children}</ReduxProvider>
</body>
</html>
);
}
Şimdi kurulumumuz bitti sayılır yapmamız gereken çok küçük bir kaç adet kaldı.
İlk olarak oluştuşmuş olduğumuz bu slice yapısını oluşturduğumuz Store içinde tanımlamamız gerekiyor.
Bunun için ilk olarak Store dosyamıza giriş yapalım ve reducer objesinin içinde oluşturduğumuz slice dosyasını ekleyelim.
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from './features/counter/counterSlice'
export const store = configureStore({
reducer: {
Mycounter: counterReducer, //Oluşturduğumuz slice. [key,Reducer]
},
});
Gördüğünüz gibi burada Mycounter adını kullanarak counterReducer’a erişmek istediğimizi store içine bildirdik.
Ufak bir dipnot eklemek istiyorum . Dosyalarımızı alt kısımda bulunan görsele göre yaptık.
Şimdi oluşturduğumuz counter slice’ını kullanarak basit bir counter uygulası geliştirelim.
//Page.js dosyamız.
import React from "react";
import Counter from "@/Components/Counter";
export default function Home() {
return (
<div className="flex flex-1 justify-center items-center h-screen">
<Counter />
</div>
);
}
//Counter componentimiz
"use client";
import React from "react";
import { useSelector } from "react-redux";
import { decrement, increment } from "../Redux/features/counter/counterSlice";
import Button from "./Button";
export default function Counter() {
const count = useSelector((state) => state.Mycounter.value);
return (
<div className="custom-number-input h-22 w-64">
<label className="w-full text-gray-700 text-sm font-semibold">
Counter Input
</label>
<div className="flex flex-row h-10 w-full rounded-lg relative bg-transparent mt-1">
<Button Text={"-"} func={decrement} />
<span className=" focus:outline-none text-center w-full bg-gray-300 font-semibold text-md hover:text-black focus:text-black md:text-basecursor-default flex items-center text-gray-700 outline-none">
{count}
</span>
<Button Text={"+"} func={increment} />
</div>
</div>
);
}
import { useSelector, useDispatch } from "react-redux";
export default function Button({ Text, func }) {
const dispatch = useDispatch();
return (
<button
onClick={() => dispatch(func())}
className=" bg-gray-300 text-gray-600 hover:text-gray-700 hover:bg-gray-400 h-full w-20 cursor-pointer outline-none"
>
<span className="m-auto text-2xl font-thin">{Text}</span>
</button>
);
}
Şimdi uygulamızı Redux DevTools üzerinden inceleyelim.
Gördüğünüz gibi her bir tıklamada state değişiyor . Child Parent ilişkisine bağlı olarak herhangi bir şekilde props gönderme işlemi gerçekleştirmedik , tamamiyle Redux üzerinden verileri aldık yada verilere increment,decrement işlemi gerçekleştirdik.
Şimdi Biraz daha advance bir çalışma gerçekleştirelim.
Öncelikle kaynak kodlarını paylaşayım ki zamandan tasarruf sağlayalım.
App klasörünün hemen altına Blog adında bir klasör oluşturalım.
https://medium.com/media/159d42c7c2334dc753e03281b6425882/hrefDaha sonra Components adı altında bir klasör daha oluşturup içine PostList PostForm ve ReactionButtons adında 3 farklı dosya oluşturalım.
PostForm dosyamız:
https://medium.com/media/3f72268f0f350b9d16ba4faeb169c789/hrefPostList Dosyamız.
https://medium.com/media/4b28d6871885136067b238fad173fe95/hrefŞimdi Slice Dosyamızı oluşturup bu slice dosyamızı store içine ekleyelim.
https://medium.com/media/1d884eaecaebe2ec44bc06e8ac50891f/href//Store dosyamız.
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./features/counter/counterSlice";
import TodoReducer from "./features/Todo/TodoSlice";
import BlogReducer from "./features/Blog/BlogSlice";
export const store = configureStore({
reducer: {
Mycounter: counterReducer,
MyTodo: TodoReducer,
Blogs: BlogReducer,
},
});
Kodumuzda ufak tefek hatalarımız söz konusu olabilir bunu bilerek yaptım şimdi beraber bunları düzenleyeceğiz ve daha advance bir şekilde projemizi oluşturacağız.Tarif ettiğim şekilde düzenlemeleri yaparsanız uygulamamız bu şekilde gözükecek.
Şimdi öncelikle düzenlemelerimize başlayalım ,
Şimdi uygulamamıza bir kaç adet yeni özellik tanımlayalım ve bu özellikler üzerinden slice üzerinde güncellemelerimizi gerçekleştirelim.
→ Öncelikle her bir post için bir adet tarih bilgisi tutalım.
→ Öncelikle her bir post için bir adet User bilgisi tutalım.
→ Öncelikle her bir post için bir adet Reaksiyon bilgisi tutalım.
Şimdi bu özellikleri öncelikle slice içinde initialState içinde ekleyelim.
Daha sonra uygulamamız içinde düzenlemelerimizi yapalım.
Öncelikle Component altında bulunan Blog dosyamızın içine yeni bir dosya ekleyelim adının ReactionButtons olarak ayarlayalım daha sonra kodlarımızı ekteki gibi düzenleyelim.
Daha sonra ilk yapmamız gereken işlem PostList içinde bu değeri component’in içine props olarak post objesini yollamak.
Şimdi User kısmına geçiş yapalım User için şöyle bir yöntem izleyelim, hem konuyu kavrama hemde konu üzerinde daha fazla pratik yapmamızı sağlayacak olan bu yöntemde başka bir slice’da yararlanarak bu işlemleri gerçekleştirelim.
Hemen Slice’ımızı oluşturalım.(Tamamen aynı yöntem olacağı için tekrar görsel üzerinden göstermek istemedim, Redux klasörü içinde features içine User Klasörü oluşturup içine bir adet UserSlice adında js dosyası oluşturduk.)
https://medium.com/media/37dfa9c9fe1224f61fdebef42fc77436/hrefŞimdi ilk olarak ReactionButtons için yaptığımız işlemlerin aynılarını PostAuthor isimli bir dosya oluşturup yeniden uygulayalım.
https://medium.com/media/04c1d1f36ea56eeeccad8e83e118155c/hrefStore dosyamızın içinde hemen UserSlice’ını tanımlayalım.
Şimdi uygulamamıza bir göz atalım.Gördüğünüz gibi değerlerimiz artık dolu gelmeye başladı. Şimdi form üzerinden bir post ekleme işlemi gerçekleştirelim.
Gördüğünüz gibi hata mesajı aldık bunun sebebi ,
Redux burada state güncellerken Emoji ve User info değerlerinin ne olduğunu bilmiyor. Biz burada redux’da yeni bir state eklerken bu değeleri henüz aylarlamadık şimdi bu değerleri ayarlamaya başlayalım.
Biz Input değeri olarak sadece Title ve Content değerlerini ele aldık.(PostForm.js dosyamızın içinde bulunan Save Methodumuz.)
Şimdi tahmin edeceğiniz üzere burada eksik alanları doldurmamız gerekiyor değil mi? Şimdi bazı alanlar manuel olacak şekilde alanları dolduralım.
Şimdi uygulamamızı tekrar kontrol edelim.
Evet başarılı bir şekilde ekleme yapabiliyoruz.
Şimdi gelelim başka bir konuya öncelikle kaç dakika önce paylaşılmış bunu belirten bir component ekleyelim projemize.
Components dosyamızın altında bulunan Blog dosyasının içinde TimeAgo adında bir javascript dosyası oluşturalım.
https://medium.com/media/5b469838575145aa76af0d61ced0a3e3/hrefDaha sonra bunu PostList içine yerleştirelim.
Şimdi uygulamamıza tekrar bi göz atalım.
Şimdi neden 10 dakika neden 5 dakika önce falan diyebiliriz,buna takılmayalım şimdilik burada bu amacımız sadece biraz dava advance özellik kazandırmak uygulamamıza.
Şimdi kullanıcı bilgilerini içeren Author Select input alanına geçiş yapalım. Burada öncelikle Author için Postform içinde bulunan Select kısmında düzenleme yapalım.
Select kısmının option kısmına Daha önce oluşturduğumuz UserSlice içinde bulunan verileri render edecek şekilde kodumuzu düzenleyelim.
Bunun için tabi UseSelector kullanarak User bilgilerini Redux’dan almamız gerekiyor.
//PostForm içinde User State değerlerini almak için gerekli.
const users = useSelector(selectAllUsers);
Şimdi User bilgilerinide redux üzerinde güncelleyecek şekilde kodumuzu düzenleyebildik.
Son bir adım kaldı Reaksiyon değerlerini güncelleme işlemi burada reducer kullanarak bu işlemi gerçekleştirelim. Zaten bu konu hakkında artık fikir sahibiyiz bundan dolayı doğrudan slice içinde reducer içinde kodumuz nasıl gözükecek onu eklemek daha mantıklı olacak.
reactionAdded adında bir reducer ekledik bu fonksiyon sayesinde gerekli olan işlemleri yapacağız.
Şimdi son olarak ReactionButton adındaki dosyamızı düzenleyelim.
Artık Uygulamamızı tamamladık şimdi optimizasyonlara başlamadan önce save için çalışan fonksiyonumuzda verileri dinamik olarak veriler gelecek şekilde düzenleyelim ve uygulamamıza bir göz gezdirelim.
Gördüğünüz kullanıcı bilgisi seçilen kullanıcıya göre ve tarih’te eklenme anına göre state içine ekleniyor ek olarak state içinde bulunan Reaction butonlarıda çalışmakta yani kısaca görüldüğü gibi her şey takırında çalışıyor :)
Optimizasyonlara başlayalım .
Şimdi PostForm dosyamızın son halini inceleyelim.
Yukarıda bulunan görselde görüldüğü gibi kodumuz aslında hiç temiz değil state sayımız artıkça AddPost reducer’i içine gönderdiğimiz sadece 3 değer var ve bizim default olarak 0 yada herhangi bir değer olarak ayarladığımız bir çok alan bulunmakta örnek olarak id değeri burada Guid kullanabiliriz fakat bu ekstra bir kütüphane gerektirecek.
Peki burada sadece bizim Redux’a gönderilen verilere göre state oluşturabilecek ve kalan verilerin ise defult değerlere sahip olacak bir şekilde bu reducer fonksiyonunu düzenlemenin bir yöntemi var mıdır sizce?
Evet var :)
Bunun için Redux Toolkit’de CreateSlice içinde bulunan Prepare methodunu kullanacağız
Prepare nedir ?
@reduxjs/toolkit içinde bulunan createSlice fonksiyonunun hemen altında bulunan prepare yöntemi, slice içinde tanımlanan reducer fonksiyonlarına uygun bir şekilde eylem nesneleri oluşturmayı kolaylaştırmak için kullanılır. Bu yöntem, genellikle slice içindeki işlevlerin temiz ve sade bir şekilde tanımlanmasına yardımcı olur.
prepare yöntemi, createSlice ile tanımlanan reducer'larınızın içinde kullanılan her bir eylemin bir eylem yaratma fonksiyonunu döndürmek için kullanılır. Bu, eylem yaratma işlemine ekstra bir soyutlama ekler ve eylem yaratma mantığını reducer işlevinden ayırır.
ilk olarak BlogSlice içine girip AddPost methodumuzu düzenleyeceğiz.
Artık PostForm içinde düzenleme yapabiliriz.
Gördüğünüz gibi kodumuz ne kadar clean bir hale geldi değil mi :)
Kısa bir süre sonra bu yazıma ek olarak Thunk yapısı Immer yapısı ve Optimizasyonlar hakkında eklemeler yapacağım.
Şimdilik hoşçakalın :)
Redux-Toolkit Thunk Yapısı
Bir Alışveriş sepeti uygulaması düşünelim , bu uygulama backend bağlantısına sahip bir sepet uygulaması olsun. Biz kendi uygulamamızda kendimiz için ihtiyacımız olan ürünleri yazmış olalım buna ek olarak eşimizde kendi bilgisayarında bu uygulama üzerinden eklemeler yapmış olsun. Şimdi bizim tutarlı bir projemiz olması için uygulamaya giriş yaptığımızda hem bizim ihtiyaçlarımız hemde eşimizin ihtiyaçlarının ekranda listelenmesi gerekiyor değil mi ? Yoksa biz alışveriş yaptıktan sonra eksik ürünler olduğunu fark edip tekrar alışverişe gitme durumunda kalabiliriz :))
İşte bu gibi durumlarda bizim uygulamamızda BackEnd üzerinden bir api ile eşimizinde eklediği ürünlere bir şekilde ulaşmamız lazım . Hmm bunun için akıllara birden fazla yöntem geliyor değil mi ama asıl konumuz Redux olduğu için Redux üzerinden ilerleyeceğiz şimdi bizim burada ihtiyacımız olan şey ne bir api call işlemi değil mi ? Hmm peki bu işlem anında gerçekleşecek bir işlem mi ? Tabikide hayır belki 1 saniye sürecek belki 10 saniye sürecek yani asenkron bir işlem olacak . Şimdi bir action gerçekleşecek gerçekleşen bu action bir şekilde api üzerinden reducer kullanarak state üzerinde bir güncelleme yapmak zorunda değil mi ?
Peki burada ne kullanacağız? Bize bir ara katman gerekiyor bu ara katman sayesinde gerekli işlem yapılıp state üzerinde değişikliklerin uygulanması gerekiyor değil mi .
Ara katman diyince aklımıza ilk gelen bir middleware değil mi ? İşte Redux burada Thunk adında bir middleware’ı bize sunuyor bu middleware sayesinde asenkron olarak işlemler yapabiliyoruz.
Şimdi işlemlere başlamadan önce teknik bir açıklama paylaşmak istiyorum sizinle;
createAsyncThunk, Redux Toolkit'in bir parçasıdır ve Redux için async işlemleri yönetmek için geliştirilmiş bir yardımcı işlevdir. Redux Toolkit, Redux'u daha verimli ve kolay kullanılabilir hale getirmeyi amaçlayan bir kütüphane ve bu kütüphane içinde yer alır.
createAsyncThunk, Redux uygulamalarında yaygın olarak kullanılan aynı döngülerin (örneğin, veri alma, gönderme veya güncelleme gibi) basit ve tekrar kullanılabilir bir yolunu sunar. Bu döngüler, genellikle üç aşamadan oluşur: "Başlat" (request), "Başarılı" (success) ve "Başarısız" (failure). createAsyncThunk, bu iş akışını kolaylaştırmak ve karmaşıklığı azaltmak için kullanılır.
Thunk yapısı üst kısımda bulunan teknik açıklamadan da anlayacağımız üzere asenkron olarak veri çekme veri gönderme gibi işlemler için kullanılan bir middleware’dır. Thunk yapısı,Async isteklerinizi bu middleware ile kullandığınızda Promise’in dönüş durumuna bağlı olarak pending, fulfilled, rejected olarak 3 farkılı dönüş sağlar bize bu dönüşler ;
pending → T anında api ile iletişime geçtiğini gösterir ve genelde loading durumlarında ekranda spinner yada skeleton göstermek için kullanılır.
fulfilled → Get yada post işlemleri için elde edilen verilerde kullanılır örnek olarak bir veri çektiğinizde request işlemi bitip response döndükten sonra state içine verileri vermek için kullanılır.
rejected → Get yada Post işlemleri ya da async olarak gerçekleşen herhangi bir işlem sonucu bir hata mesajı alındığı zaman yürürlüğe girer.
Şimdi beraber bu thunk middleware’ını kullanarak bir ürün listesi oluşturalım.
Kaynak kodlarını github üzerinden Thunk yapısı isimli commit üzerinden ulaşabilirsiniz.
Thunk yapısını kullanma
Şimdi ilk olarak tabikide bir slice’a ihtiyacımız olacak. Öncelikle slice oluşturmaya başlayalım.
Hemen Projemiz içinde bulunan Redux dosyamızın altında bulunan features dosyamızın içine Products adında bir dosya oluşturalım daha sonra ProductSlice adında bir javascript dosyası oluşturalım .
Daha önce oluşturduğumuz slice yapısı ile tamamen aynı şekilde ilerleyeceğiz ufak bir farklılık var middleware için kullanacağımız.
İlk olarak initialstate’i oluşturalım.
Burada daha önce oluşturduğumuz initialState objelerine göre ufak bir farklılığa sahip olmamız gerekiyor pending , fulfilled ve rejected durumlarına göre neler olup bittiğini kullanıcıya yansıtabilmemiz için isLoading ve isError olmak üzere 2 farklı durumu state üzerine eklememiz gerekiyor ki middleware’den tam olarak yararlanabilelim.
const initialState = {
productList: [], //Ürünlerin listeleneceği alan
isLoading: null, //Loading durumuna erişmemiz için
isError: null, //Hata varsa hata ile ilgili duruma erişmemiz için
};
Şimdi slice yapımızı oluşturalım.
export const ProductSlice = createSlice({
name: "Product",
initialState,
reducers: {},
});
Şimdi asenkron olarak veri çekme işlemi için Thunk yapımızı oluşturalım.
Oluşturacağımız bu yapıyı mutlaka Export etmemiz gerekiyor çünkü bu yapıyı kullanarak api call işlemini tetikleyeceğiz.
export const FetchProduct = createAsyncThunk();
Şimdi bu yapı bizden 2 farklı parametre beklemektedir.
Bunlar typePrefix ve payloadCreator.
typePrefix, createAsyncThunk ile oluşturulan üç farklı Redux eylemini birbirinden ayırt etmek için kullanılan bir başlangıç ismidir. Bu başlangıç ismi, üç farklı eylem türünü belirlemek için kullanılır. Bu üç tür:
- İşlem başlatma (Pending): Bu, asenkron işlem başladığında gönderilen eylemdir.
- İşlem başarıyla tamamlandı (Fulfilled): Bu, asenkron işlem başarıyla tamamlandığında gönderilen eylemdir.
- İşlem hata ile sonuçlandı (Rejected): Bu, asenkron işlem bir hatayla sonuçlandığında gönderilen eylemdir.
typePrefix, bu üç eylemin türlerine başlangıç ismi verir. Örneğin, eğer typePrefix "todos/fetch" ise, otomatik olarak aşağıdaki eylem türleri oluşturulur:
- “todos/fetch/pending” (İşlem başlatma)
- “todos/fetch/fulfilled” (İşlem başarıyla tamamlandı)
- “todos/fetch/rejected” (İşlem hata ile sonuçlandı)
Bu şekilde, her bir eylem türü, hangi işlemin başladığını, başarıyla tamamlandığını veya hata ile sonuçlandığını ayırt etmek için kullanılır.
Genelde isimlendirmeleri şu şekilde yapıyorlar ;
“SLİCE_İSMİ” / “Thunk Assign eden Değişken”
export const FetchProduct = createAsyncThunk(
"Product/FetchProduct",
);
Şimdi ikinci parametremiz olan payloadCreator nedir buna bakalım.
payloadCreator: Bu işlev, asenkron işlem sonucu olarak dönen veriyi oluşturur. Daha basit bir diyinle api call işlemini yapan fonksiyon.
const FetchProductAsynFunction = async () => {
const res = await axios("https://fakestoreapi.com/products");
const data = await res.data;
return data;
};
export const FetchProduct = createAsyncThunk(
"Product/FetchProduct",
FetchProductAsynFunction
);
//Başka bir kullanım türü şeklinde (Genelde böyle kullanılır).
export const FetchProduct = createAsyncThunk(
"Product/FetchProduct", async () => {
const res = await axios('https://fakestoreapi.com/products')
const data = await res.data
return data
});
Şimdi Slice ve Thunk yapısını oluşturduk . Artık bu Thunk yapısına göre bahsettiğimiz 3 ana durum (pending,fulfilled ve rejected) için reducer hazırlamamız gerekiyor.
Şimdi bu işlem için öncelikle Slice içinde düzenleme yapmamız gerekmekte. Bir kaç paragraf üstte size ufak bir fark olduğundan bahsetmiştim şimdi o farklılığa geldik.
Reducerları oluştururken burada reducers objesi içinde değil extraReducers parametersini kullanacağız.
export const ProductSlice = createSlice({
// Slice ismi belirtiliyor
name: "Product",
// Başlangıç state'i ayarlanıyor
initialState,
// Reducer ayarlamaları, burada herhangi bir ayarlama yapılmamaktadır.
reducers: {},
// Ekstra reducerler Thunk yapısı için tanımlandı.
extraReducers: (builder) => {
// Ürün detaylarının alınması durumunda çalışacak function
builder.addCase(FetchProduct.pending, (state) => {
// Verinin yüklenmesinin durumu provoke edilir
state.isLoading = true;
});
// Ürünlerin başarılı bir şekilde alınması durumu
builder.addCase(FetchProduct.fulfilled, (state, action) => {
// Yüklenme durumu false olur ve ürünler state'e yerleştirilir.
state.isLoading = false;
state.productList = action.payload;
});
// Ürünlerin alınamaması durumu
builder.addCase(FetchProduct.rejected, (state, action) => {
// Yüklenme durumu false olur ve hata bilgisi state'e yerleştirilir.
state.isLoading = false;
state.isError = action.error.message;
});
},
});
Thunk yapısı yapmamız gereken bütün geliştirmeler bitti. Şimdi bu işlem için bir UI geliştirelim.
https://medium.com/media/9ce8317a1a7bc0b285b1d81ee5c80958/hrefFetchProduct → Bizim Asyn Fonksiyonumuz bunu Thunk’ın tetiklenmesi için export etmemiz gerektiğini daha önce size belirmiştim.
Şimdi projemize göz atalım.
Thunk middleware’i hakkında belirmem gereken fakat ben şimdiye kadar hiç kullanıldığı bir projeye açıkçası denk gelmediğim bir konu daha var ,
Thunk fonksiyonu üçüncü bir parametre daha kabul ediyor bu parametre options.İsimden de anlaşılacağını üzere özelleştirme sağlıyor bu parametre ile bizler obje içinde özelleştirmeler yapabiliriz ,
Şimdi ilk olarak kendi oluşturduğumuz Thunk yapısı üzerinden konuları inceleyelim.
export const FetchProduct = createAsyncThunk(
"Product/FetchProduct",
FetchProductAsynFunction,
{condition: .....}
);
1- Condition (isteğe bağlı): Bu işlev, asenkron işlemi başlatmadan önce belirli bir koşulu kontrol etmenizi sağlar. Eğer condition işlevi false dönerse, işlem başlatılmaz. Bu, belirli koşulların sağlanmadığı durumlarda işlemin engellenmesi için kullanışlıdır. Örnek olarak daha önce veriler çekilmiş ise ve state içinde eklenmiş ise tekrar verilerin çekilmesini engellemek için kullanılabilir.
const FetchProduct = createAsyncThunk(
"Product/FetchProduct",
async () => {
const res = await axios("https://fakestoreapi.com/products");
const data = await res.data;
return data;
},
{
condition: (arg, { getState }) => {
const { products } = getState();
// Eğer ürünler zaten yüklenmişse, tekrar yüklemeyi atla
if (products.length > 0) {
return false;
}
},
}
);
2- DispatchConditionRejection: condition() işlevi false döndürürse, varsayılan davranış hiçbir işlemin gönderilmemesidir. Ancak, işlem iptal edildiğinde hala "reddedilmiş" bir işlemin gönderilmesini istiyorsanız, bu bayrağı true olarak ayarlamak gerekir.
const FetchProduct = createAsyncThunk(
"Product/FetchProduct",
async () => {
const res = await axios("https://fakestoreapi.com/products");
const data = await res.data;
return data;
},
{
condition: (arg, { getState }) => {
const { products } = getState();
if (products.length > 0) {
return false;
}
},
dispatchConditionRejection: true,
}
);
3- idGenerator(arg) : Bu işlev, istek sırası için requestId oluştururken kullanılır. Varsayılan olarak nanoid kullanır, ancak kendi ID oluşturma mantığınızı uygulayabilirsiniz.
const FetchProduct = createAsyncThunk(
"Product/FetchProduct",
async () => {
const res = await axios("https://fakestoreapi.com/products");
const data = await res.data;
return data;
},
{
idGenerator: () => "uniqueId", // Kendi ID oluşturma mantığınız
}
);
4- SerializeError: Bu işlev, istek sırası için requestId oluştururken kullanılır. Varsayılan olarak nanoid kullanır, ancak kendi ID oluşturma mantığınızı uygulayabilirsiniz.
const FetchProduct = createAsyncThunk(
"Product/FetchProduct",
async () => {
const res = await axios("https://fakestoreapi.com/products");
const data = await res.data;
return data;
},
{
// Kendi hata serileştirme mantığınız
serializeError: (error) => error.message,
}
);
5- getPendingMeta: Bu işlev, pendingAction.meta alanına birleştirilecek bir nesne oluşturmak için kullanılır.
const FetchProduct = createAsyncThunk(
"Product/FetchProduct",
async () => {
const res = await axios("https://fakestoreapi.com/products");
const data = await res.data;
return data;
},
{
getPendingMeta: ({ arg, requestId }, { getState, extra }) => {
// Kendi meta verinizi oluşturun
return {
customMetaKey: "customMetaData",
};
},
}
);
Projemizin Github Linki : https://github.com/kubilaybzk/redux-toolkit-medium
Kaynaklar:
https://akursat.medium.com/redux-toolkit-6a49f5eab539
https://medium.com/@furkansunger/redux-toolkite-giris-953a57860d8c
https://redux-toolkit.js.org/
https://academy.patika.dev/courses/redux

Native mobil uygulama yazmak isteyen NextJS geliştiricileri için velinimet olan Expo Router yapısı ile File-Based Routing işlemleri.
Herkese merhaba uzun zaman sonra yeni bir içerik ile karşınızdayım son zamanlarda mobile biraz daha fazla ilgi duymaya başladım ve Native olarak uygulamalar geliştirmek için dökümanları incelerken fark ettiğim NextJS hayranı olan ve ReactRouting işlemlerini sevmeyen file-based routing işlemleri ile kod geliştirmeyi benim gibi seven kullanıcılar için veli nimet olduğunu düşündüğüm Expo Routing hakkında tutmuş olduğum notları sizinle paylaşmak istedim.
Öncelikle örnek bir proje oluşturalım bunun için hazır template yapısının ve konfigürasyonunun olduğu bir proje oluşturalım.
npx create-react-native-app -t with-router
Şimdi projemizi oluşturduktan sonra ilk olarak projemizin yapısını inceleyelim.
Projemizi oluşturduktan sonra dosya yapımıza dikkat ederseniz bir adet index.js dosyamız mevcut bu dosya içine baktığımız zaman tek satır bir kod göreceğiz basit bir anlatım yaparsak burada expo’nun router yapısını
import “expo-router/entry”;
Şimdi projemizi başlatalım ve nasıl bir cıktı aldığımıza göz atalım.
Gördüğünüz gibi Expo tarafından default olarak hazırlanan ekran bizim karşımıza çıktı şimdi ilk olarak uygulamanın alt kısmında bulunan
beyaz alana tıklayalım ve ne olacağını inceleyelim.
Görüldüğü gibi App adında bir klasör eklendi ve içinde index.js olarak bir dosya oluştu bu index.js bizim için uygulama açıldığı zaman kullanıcının göreceği ilk sayfa olacak. Şimdi yavaştan işlemlere başlayalım.
Statick Routing
Şimdi App dosyamızın içine bir adet profile isimli bir js dosyası oluşturalım ve index.js dosyamızı güncelleyelim. Kodlar uzun olmasın diye stillerimizi bilerek eklemedim kodlarımız uzamasın.
//Profile.js
import React from "react";
import { Link } from "expo-router";
import { Button } from "react-native";
export default function Profile() {
return (
<View style={styles.container}>
<View style={styles.main}>
<Text style={styles.title}>Profile Page</Text>
<Text style={styles.subtitle}>This is the first page of your app.</Text>
<Link style={styles.mybutton} href={"/"}>
<Text>Go Home</Text>
</Link>
</View>
</View>
);
}
//Index.js dosyamız.
import { Link } from "expo-router";
import { StyleSheet, Text, View } from "react-native";
export default function Page() {
return (
<View style={styles.container}>
<View style={styles.main}>
<Text style={styles.title}>Hello World</Text>
<Text style={styles.subtitle}>This is the first page of your app.</Text>
<Link style={styles.mybutton} href={"/profile"}>
<Text>Go Profile</Text>
</Link>
</View>
</View>
);
}
Şimdi sonuçları inceleyelim projemizi şimdi inceleyelim .
Gördüğünüz gibi iki adet sayfa oluşturduk ve routing işlemlerini Router Yapısında bulunan Link component’i sayesinde hallettik. NextJS’in yapısına ne kadar benzer ve güzel değil mi :)
Şimdi useRouter kullanarak geri gitme işlemimizi gerçekleştirelim.
Projemizde tekrar App dosyasının altında settings.js adında bir dosya oluşturalım ve useRouter kullanarak geri gitme işlemini yapalım.
//settings.js dosyamız.
import { View, Text,StyleSheet } from "react-native";
import React from "react";
import { Link } from "expo-router";
import { useRouter } from "expo-router";
import { Button } from "react-native";
export default function Settings() {
let router=useRouter();
return (
<View style={styles.container}>
<View style={styles.main}>
<Text style={styles.title}>Settings Page</Text>
<Text style={styles.subtitle}>This is the first page of your app.</Text>
<Button style={styles.mybutton} onPress={()=>router.back()} title="Go Back"/></View>
</View>
);
}
NextJS içinde bulunan UseRouter hook’una ne kadar benzer olduğuna dikkat ettiniz mi :) Projemizi sonucuna tekrar bakalım …
SearchParams kullanarak Api’den veri çekme işlemleri.
Öncelikle şunu belirtmek isterim search parametrelerini bu örneğimizde normal şartlarda doğru bir şekilde kullanmayacağız .Konularda adım adım gitmek istediğim için dynamic olarak routing işlemlerinden henüz bahsetmediğimiz için burada bu konuyu göz ardı etmenizi rica ediyorum.
Şimdi projemizde iki adet yeni dosya oluşturalım.
//AddressBook.js dosyamız.
import { Link, useRouter, useSearchParams } from "expo-router";
import React, { useEffect, useState } from "react";
import { Image } from "react-native";
import { SafeAreaView } from "react-native";
import { ScrollView } from "react-native";
import { View, Text, StyleSheet, Button } from "react-native";
export default function AddressBook() {
let navigation = useRouter();
const [data, setData] = useState([]);
const getPosts = async () => {
console.log("work !! ");
try {
const response = await fetch(
"https://random-data-api.com/api/users/random_user?size=50"
);
const json = await response.json();
setData(json);
} catch (error) {
console.error(error);
}
};
useEffect(() => {
getPosts();
}, []);
//Yukarı kaydırınca yeniden api'den yeni verileri çekme işlemi.
const handleScroll = ({ nativeEvent }) => {
const { contentOffset, layoutMeasurement, contentSize } = nativeEvent;
const isScrolledToTop = contentOffset.y === 0;
const isNearBottom =
layoutMeasurement.height + contentOffset.y >= contentSize.height - 20;
if (isScrolledToTop) {
getPosts();
}
};
return (
<SafeAreaView className="flex flex-col justify-center items-center align-middle">
<View className="flex flex-row items-center justify-between w-full px-2">
<Text className="text-2xl text-red-400">Address Book</Text>
<Button onPress={() => navigation.back()} title="Go Back" />
</View>
<ScrollView
className="mt-4 p-2 w-full"
indicatorStyle="white"
contentInset={{ bottom: "#000000" }}
onScroll={handleScroll}
>
{data &&
data.map((post) => {
return (
<Link
key={post.id}
href={`SingleBook?UserID=${post.id}`}
className=" mb-3 p-3 w-full"
>
<View className="flex flex-row w-full gap-2">
<Image
className="rounded-full w-12 h-12 border-2"
source={{
uri: post.avatar,
}}
/>
<View className="w-fit">
<Text className="text-blue-400">{post.username}</Text>
<View className="d-flex flex-row flex-wrap w-fit">
<Text>{post.address.city}</Text>
<Text>{post.address.street_name}</Text>
<Text>{post.address.street_address}</Text>
<Text>{post.address.country}</Text>
</View>
</View>
</View>
</Link>
);
})}
</ScrollView>
</SafeAreaView>
);
}
//SingleBook.js dosyamız.
import { Link, useRouter, useSearchParams } from "expo-router";
import React, { useEffect, useState } from "react";
import { Image } from "react-native";
import { SafeAreaView } from "react-native";
import { ScrollView } from "react-native";
import { View, Text, StyleSheet, Button } from "react-native";
export default function AddressBook() {
let navigation = useRouter();
let params = useSearchParams();
const [data, setData] = useState([]);
const getPosts = async () => {
try {
const response = await fetch(
`https://random-data-api.com/api/users/random_user?UserId=${params.UserID}`
);
const json = await response.json();
setData(json);
} catch (error) {
console.error(error);
}
};
useEffect(() => {
getPosts();
}, []);
let ContextStyle = "border-b-2 bg-red-400";
return (
<SafeAreaView className="flex flex-col justify-center items-center align-middle">
<View className="flex flex-row items-center justify-between w-full px-2">
<Text className="text-xl text-red-400">{data.username}'s details</Text>
<Button onPress={() => navigation.back()} title="Go Back" />
</View>
<ScrollView
className="mt-4 p-2 w-full"
indicatorStyle="white"
contentInset={{ bottom: "#000000" }}
>
<View className=" mb-3 p-3 w-full space-y-5">
<View className="flex flex-col w-full justify-center items-center gap-2">
<Image
className="rounded-full w-24 h-24 border-2"
source={{
uri: data.avatar,
}}
/>
<View className="w-full text-center justify-center items-center ">
<Text className="text-blue-400 text-base">{data.username}</Text>
<View className="flex flex-col mt-2 w-full text-center items-center justify-center">
<Text>{data.address && data.address.city}</Text>
<Text>{data.address && data.address.street_name}</Text>
<Text>{data.address && data.address.street_address}</Text>
<Text>{data.address && data.address.country}</Text>
</View>
</View>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
}
Burada stillendirme olarak NativeWind kullanmayı tercih ettim kendisi NextJS kullanırken kelimenin tam anlamıyla aşık olduğum bir CSS framework diyebilirim. İncelemenizi tavsiye ederim.
Konuyu çok fazla uzatmadan devam edelim şimdi uygulamamızın genel dosyalarını incelemeden önce bir sonucumuza göz atalım isterseniz.
Şimdi eklemiş olduğumuz AddressBook isimli komponent bizim için bütün dataların listesini tutuyor. SingleBook ise sadece seçilen bir kullanıcının bilgilerini dönüyor.
const response = await fetch(
"https://random-data-api.com/api/users/random_user?size=50"
);
const response = await fetch(
`https://random-data-api.com/api/users/random_user?UserId=${params.UserID}`
);
Yukarıda görmüş olduğunuz ilk linkimizi biz random 50 adet data çekerken kullanıyoruz,
İkinci api’ ise bizim için UserId değerine göre o UserId değerine sahip kullanıcının bilgilerini tek bir obje olarak döndürüyor. (normal şartlarda random olarak döndürüyor yani seçilen kullanıcının bilgilerini vermiyor fakat biz öyle gibi düşünelim.)
NextJS olarak düşünelim.
localhots:3000/AddressBook =>50 adet kullanıcı bilgileri tutan listemiz.
localhots:3000/SingleBook?UserId=”1” => Tek bir kullanıcının bilgilerini görüyoruz.
Burada userID değerini nasıl elde ettiğimizi inceleyelim.
//Burada useSearchParams hook'unu kullanarak parametreleri elde edebiliyoruz.
let params = useSearchParams();
const response = await fetch(
`https://random-data-api.com/api/users/random_user?UserId=${params.UserID}`
);
Şimdi ise Dynamic Routing nasıl çalışır onu inceleyelim
NextJS’de Dynamic routing yapısında hatırlarsanız dosyamız [slug].js yada [id].js gibi isimler veriyorduk. Yada klasör adını [dynamic_isim] gibi ayarlıyorduk. Görsel olarak bakmak gerekir ise ,
Burada şöyle bir senaryomuz var bizim 4adet klasörümüz mevcut .
[slug] , Products , User ve SingleProduct olmak üzere bu rout yapılarımız hakkında basit bir özet geçmek gerekirse,
localhost:3000/Product => Ürün listemizi verir.
localhost:3000/SingleProduct/[id] => Her bir ürünün detayını alır
Örnek olarak ;
localhost:3000/SingleProduct/1 => bir telefon olurken
localhost:3000/SingleProduct/12 = > bir atıştırma olabilir.
slug dosyası sayesinde ise ,
www.localhotst:3000/Çilek
www.localhotst:3000/Araba
www.localhotst:3000/Jeep213
gibi web sitlerini tek bir sayfada açabiliriz.
Yada User dosyası içinde bulunan index.js ve [id].js ile iç içe routing yapısı kurabiliriz.
www.localhotst:3000/User => Kullanıcının kendi sayfasını gösterirken
www.localhotst:3000/Araba => Başka bir kullanıcı hakkında bilgi veren bir sayfa olabilir.
Şimdi burada kodlar üzerinden inceleme yapalım
//Products dosyamızın altında bulunan index.js dosyamız.
import { View, Text, SafeAreaView, StyleSheet } from "react-native";
import React, { useEffect, useState } from "react";
import { Link, useRouter } from "expo-router";
import { SectionList } from "react-native";
import { ActivityIndicator } from "react-native";
import { ScrollView } from "react-native";
import { Image } from "react-native";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { FlatList } from "react-native";
import { TouchableOpacity } from "react-native";
export default function TwitterHomePage() {
let router = useRouter();
const insets = useSafeAreaInsets();
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
const getMovies = async () => {
try {
const response = await fetch(
"https://dummyjson.com/products?limit=10&skip=0"
);
const json = await response.json();
setData(json);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
getMovies();
}, []);
if (isLoading) {
return <ActivityIndicator size="large" color="#fff" />;
} else {
return (
<SafeAreaView
style={{
paddingTop: insets.top + 12,
paddingLeft: insets.left + 24,
paddingRight: insets.right + 24,
paddingBottom: insets.bottom + 12,
}}
>
<View className="border-b mb-4">
<Text className="text-lg font-black">Product List</Text>
</View>
<FlatList
className="p-2"
scrollsToTop={true}
data={data.products}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => router.push(`/SingleProduct/${item.id}`)}
>
<View className="mt-3 flex-1 flex-row-reverse gap-w border border-Gray1 p-2 rounded flex justify-center items-center">
<Image
className="border border-blue-400"
source={{ uri: item.images[1] }}
style={{ width: 60, height: 60, borderRadius: 30 }}
/>
<View className="flex-1 flex-col">
<Text className="font-bold color-Primary">{item.title}</Text>
<Text
numberOfLines={2}
ellipsizeMode="tail"
className="font-normal"
>
{item.description}
</Text>
<View className="flex-1 mt-2 flex-row justify-between">
<Text className="text-Blue1">{item.price} TL</Text>
<Text>{item.price}TL</Text>
</View>
</View>
</View>
</TouchableOpacity>
)}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
);
}
}
const randomColor = () => {
const colors = [
"bg-red-200",
"bg-blue-200",
"bg-green-200",
"bg-yellow-200",
"bg-indigo-200",
"bg-purple-200",
"bg-pink-200",
"bg-teal-200",
"bg-orange-200",
];
const randomIndex = Math.floor(Math.random() * colors.length);
return colors[randomIndex];
};
const generateRandomColor = () => {
const randomBgColor = randomColor();
return randomBgColor;
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#F7F7F7",
marginTop: 60,
},
listItem: {
margin: 10,
padding: 10,
backgroundColor: "#FFF",
width: "80%",
flex: 1,
alignSelf: "center",
flexDirection: "row",
borderRadius: 5,
},
});
function Item(item) {
console.log(item);
return <Text>{JSON.stringify(item)}</Text>;
}
// SingleProduct dosyamızın içinde bulunan [id].js dosyamız.
import {
View,
Text,
SafeAreaView,
StyleSheet,
TouchableHighlight,
} from "react-native";
import React, { useEffect, useState } from "react";
import { useLocalSearchParams, useRouter } from "expo-router";
import { ActivityIndicator } from "react-native";
import { ScrollView } from "react-native";
import { Image } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { Button } from "react-native";
export default function TwitterHomePage() {
let { id } = useLocalSearchParams();
let router = useRouter();
const insets = useSafeAreaInsets();
const [isLoading, setLoading] = useState(true);
const [data, setData] = useState([]);
const getMovies = async () => {
try {
const response = await fetch(`https://dummyjson.com/products/${id}`);
const json = await response.json();
setData(json);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
getMovies();
}, []);
if (isLoading) {
return <ActivityIndicator size="large" color="#fff" />;
} else {
return (
<SafeAreaView
style={{
paddingTop: insets.top,
paddingLeft: insets.left + 24,
paddingRight: insets.right + 24,
paddingBottom: insets.bottom + 12,
}}
>
<View className="border-b mb-4">
<Text className="text-lg font-black">Single Product</Text>
</View>
<View
style={{
backgroundColor: "#ffb500",
position: "relative",
height: 100,
justifyContent: "flex-start",
paddingTop: 12,
gap: 12,
alignItems: "center",
}}
>
<Text className="text-white font-bold">{data.title}</Text>
<View className="bg-Primary p-4 -mb-[100px] rounded-3xl border-black border-2">
<Image
source={{ uri: data.images[0] }}
style={{
resizeMode: "contain",
height: 100,
width: 100,
padding: 12,
backgroundColor: "#ffb500",
border: 2,
}}
/>
</View>
</View>
<ScrollView className="mt-[100px]">
<Text
className="bg-Gray4 text-center text-white rounded uppercase p-0.5"
style={{ maxWidth: "40%" }}
>
{data.category}
</Text>
<Text className="font-bold text-base">{data.brand}</Text>
<Text className="font-medium text-base text-black/75">
{data.title}
</Text>
<View className=" w-fit justify-end items-end rounded-lg">
<Text className="font-bold text-lg text-white bg-Green2 w-24 text-center rounded-lg ">
{data.price} TL
</Text>
</View>
<View className="flex flex-1">
<Text>Total Star: {data.rating}</Text>
</View>
<TouchableHighlight
onPress={() => router.back()}
className="bg-Green2 mt-4 px-1 py-2"
>
<Text className="text-center text-xl text-white">Sepete Ekle</Text>
</TouchableHighlight>
</ScrollView>
</SafeAreaView>
);
}
}
Burada id değerine göre o ürünün detaylarına geçen bir E-Ticaret uygulaması yaptığımızı düşünelim.
let { id } = useLocalSearchParams();
Sayesinde O’an hangi sayfada olduğumuzu yani [id].js router dosyamızda id değerini karşılayan değeri öğrenmek için bu yöntemi kullanıyoruz. NextJS içinde bulunan 13. versiyon ile birlikte hayatımıza usePathName hook’u ile aynı görevi yapıyor .
Anlık Routumz : localhost:3000/SingleProduct/1
olsun burada api’a id değerini yollayarak istenen ürün için detay bilgilerini alıp ekrana bastığımızı unutmayalım. Kodumuzun çalışan halini inceleyelim.
Gördüğünüz gibi Go Product butonuna tıkladığımız zaman Products isimli Rout yolumuza gidiyoruz , burada ürünlerimizin listesi mevcut.
Daha sonra buradan , useRouter hook’unu kullanarak TouchableOpacity
‘nin içinde bulunan OnPress methodu ile nasıl bir sayfadan başka bir sayfaya geçiş yaparız onu inceleyelim.
let router = useRouter(); //import etmemiz gereken hookumuz
<TouchableOpacity
onPress={() => router.push(`/SingleProduct/${item.id}`)}>
//......
</TouchableOpacity>
Gördüğünüz gibi burada onPresMethodu sayesinde aktif olan sayfamızı değiştirdik.
Bunun dışında başka bir yöntem olarak Link’de kullanabiliriz. NextJS’in Link yapısına ne kadar çok benzediğini burada görebilirsiniz.
import { Link, usePathname } from "expo-router";
<Link
className="bg-Primary w-full mt-5 text-white text-center p-4 text-lg"
href={"/"}>
<Text>Go Home Page</Text>
</Link>
Şimdi User Sayfasını incleyelim. Yani iç içe Routing yapısına bakalım
//User/index.js dosyamız
import { Link } from "expo-router";
import React from "react";
import { StyleSheet } from "react-native";
import { SafeAreaView, Text, Linking } from "react-native";
const generateRandomUsername = () => {
const usernames = ["Router1", "Router2", "Router3","Router4","Router5","Router6"];
const randomIndex = Math.floor(Math.random() * usernames.length);
return usernames[randomIndex];
};
const UserHomePage = () => {
const handleLinkClick = () => {
const randomUsername = generateRandomUsername();
const url = `/User/${randomUsername}`;
return url;
};
return (
<SafeAreaView>
<Text>UserHomePage</Text>
<Link style={styles.mybutton} href={handleLinkClick()}>
<Text>Go RandomUser</Text>
</Link>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
mybutton: {
fontSize: 23,
padding: 3,
marginTop: 12,
backgroundColor: "#ffb500",
textAlign: "center",
borderRadius: 5,
},
});
export default UserHomePage;
// User altında bulunan [id].js dosyası
import { View, Text } from "react-native";
import React from "react";
import { Link, usePathname } from "expo-router";
import { StyleSheet } from "react-native";
import { SafeAreaView } from "react-native";
export default function UserPageWithSlug() {
let params = usePathname();
console.log(params);
return (
<SafeAreaView>
<Text>UserName:{JSON.stringify(params)} </Text>
<Link style={styles.mybutton} href={"/User"}>
<Text>Go User</Text>
</Link>
<Link style={styles.mybutton} href={"/"}>
<Text>Go HomePage</Text>
</Link>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
mybutton: {
fontSize: 23,
padding: 3,
marginTop: 12,
backgroundColor: "#ffb500",
textAlign: "center",
borderRadius: 5,
},
});
Üst tarafta bulunan Username: kısmında aktif olan path adresimiz’i gösteriyoruz.
Dosyamızın adınının [slug] olduğu duruma göz atalım.
// [slug]/index.js dosyamız.
import { View, Text } from "react-native";
import React from "react";
import { SafeAreaView } from "react-native";
import { Link, usePathname } from "expo-router";
export default function DynamicFolder() {
const generateRandomPage = () => {
const routes = ["Route1", "Route2", "Route3", "Route4", "Route5", "Route6"];
const randomIndex = Math.floor(Math.random() * routes.length);
return routes[randomIndex];
};
let router = usePathname();
return (
<SafeAreaView>
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center",
}}
>
<Link
className="bg-Primary w-full text-white text-center p-4 text-lg"
href={generateRandomPage()}
>
<Text>Go Random Page</Text>
</Link>
<Text className="font-bold text-2xl mt-4">
Şuan bulunduğunuz Route :{" "}
</Text>
<View className="border-red-600 border w-full p-4 text-lg mt-4">
<Text className="text-center font-bold">
{JSON.stringify(router)}
</Text>
</View>
<Link
className="bg-Primary w-full mt-5 text-white text-center p-4 text-lg"
href={"/"}
>
<Text>Go Home Page</Text>
</Link>
</View>
</SafeAreaView>
);
}
Peki Screenler’in nasıl ayarlayacağız onun üzerine konuşalım daha önce react-navigation üzerinden nasıl Stack Navigation , Tabs Navigation ve Drawer navigation hakkında yazılar yazmıştım. Şimdi bunun expo için olan versiyonunu inceleyelim.
Stack Navigation
İki farklı kullanım mevcut . İki farklı yöntemi’de burada inceleyeceğiz.
İlk olarak navigasyon işlemlerini NextJS üzerinden düşünelim ,NextJS 13 ile birlikte layouth.js adında ek bir module ortaya çıktı bu modül uygulamamızda ortak bir layouth kullanmamıza olanak sağladı. Genelde bu dosya içinde Provider’lar ve her sayfada gözükecek olan componentlerin ekrana render edilmesi için kullanıyoruz, aslında benzer olarak düşünebiliriz benzer değil (bu konuda linç yemek istemiyorum :) ).
Öncelikle genel kurulumları yapalım.
İlk olarak projemizde bulunan bütün dosyaları silelim sadece index.js dosyamız kalsın ve index.js dosyamızı güncelleyelim.
import { Link } from "expo-router";
import { SafeAreaView } from "react-native";
import { StyleSheet, Text, View } from "react-native";
export default function Page() {
return (
<SafeAreaView>
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center",
}}
>
<Text className="text-lg font-bold"> Stack Navigation </Text>
<View className="flex flex-col space-y-5 mt-4 w-11/12 justify-center items-center text-center">
<Link className="bg-Primary text-base text-center w-full text-white p-2" href={"/kubilay"}>Kubilay's Page</Link>
<Link className="bg-Primary text-base text-center w-full text-white p-2" href={"/Ali"}>Ali's Page</Link>
<Link className="bg-Primary text-base text-center w-full text-white p-2" href={"/Veli"}>Veli's Page</Link>
</View>
</View>
</SafeAreaView>
);
}
App dizinin hemen altına bir adet dosya ekleyelim. [username].js olarak isimlendirelim.
import { View, Text } from "react-native";
import React from "react";
import { Link, usePathname } from "expo-router";
import { StyleSheet } from "react-native";
import { SafeAreaView } from "react-native";
export default function UserPageWithSlug() {
let params = usePathname();
console.log(params);
return (
<SafeAreaView>
<View className=" h-full w-full justify-center align-middle items-center">
<Text className="text-lg font-bold border border-red-500 p-2">
<Text className="text-blue-500">UserName: </Text>
{JSON.stringify(params)}
</Text>
<Link
className="w-full bg-Primary text-white mt-4 p-3 text-center text-base border-1 border-black"
href={"/User"}
>
<Text>Go User</Text>
</Link>
<Link
className="w-full bg-Primary text-white mt-4 p-3 text-center text-base border-1 border-black"
href={"/"}
>
<Text>Go HomePage</Text>
</Link>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
mybutton: {
fontSize: 23,
padding: 3,
marginTop: 12,
backgroundColor: "#ffb500",
textAlign: "center",
borderRadius: 5,
},
});
Şimdi burada son olarak yapmamız gereken tekrar bir adet dosya eklemek .
_layout.js isimi verdiğimiz bir dosya oluşturuyoruz.
Bu dosya sayesinde hangi Navigasyon türünü kullanacağımızı belirtiyoruz .
Daha önceki yazılarımda Stack Navigation kullanırken Stack.Screen kullanarak component ve title değerlerini giriyorduk .
Bu yöntemde, Expo sayesinde Stack.Screen kullanıp içeride title ve component bilgilerini vermemize gerek yok.
//_layout.js dosyamız
import { Stack } from "expo-router";
export default () => {
return <Stack></Stack>;
};
Projemizi çalıştırdığımız zaman karşımıza çıkan ekranı inceleyelim.
Gördüğünüz gibi hiçbir şekilde Stack.Screen kullanmadık ve Expo bizim için kendi router yapısını kullanarak Stack.Screenleri otomatik olarak tanımladı.
Şimdi Stack Navigasyon işlemimizi biraz özelleştirelim.
//_layout.js dosyamız
import { Stack } from "expo-router";
export default () => {
return (
<Stack
screenOptions={{
headerStyle: {
backgroundColor: "blue",
},
headerTintColor: "white",
headerTitleStyle: {
fontWeight: "bold",
},
}}
></Stack>
);
};
Peki biz bu Stack ekranlarında sadece, header kısımlarını sayfalara özel olarak değiştirmek istersek ne yapabiliriz.
1-)Hedef dosyaya gidip dosya içinde Stack.Screen tanımlamak.
2-)_layout.js içinde Stack.Screen tanımlamak.
Bu iki yöntemin avantaj ve dezavantajları mevcut.
1-)Hedef dosyaya gidip dosya içinde Stack.Screen tanımlamak.
Mesela şöyle bir yöntem izleyelim , AnaSayfa’da yani uygulama ilk açıldığı zaman Header’ın içinde index yazısını Home Page olarak değiştirmek isteyelim.
<Stack.Screen options={{ title: "HomePage" }} />
Kodumuzda herhangi bir yere yerleştirelim.
//index.js dosyam
import { Link, Stack } from "expo-router";
import { SafeAreaView } from "react-native";
import { StyleSheet, Text, View } from "react-native";
export default function Page() {
return (
<SafeAreaView>
<Stack.Screen options={{ title: "HomePage" }} />
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center",
}}
>
<Text className="text-lg font-bold"> Stack Navigation </Text>
<View className="flex flex-col space-y-5 mt-4 w-11/12 justify-center items-center text-center">
<Link
className="bg-Primary text-base text-center w-full text-white p-2"
href={"/kubilay"}
>
Kubilay's Page
</Link>
<Link
className="bg-Primary text-base text-center w-full text-white p-2"
href={"/Ali"}
>
Ali's Page
</Link>
<Link
className="bg-Primary text-base text-center w-full text-white p-2"
href={"/Veli"}
>
Veli's Page
</Link>
</View>
</View>
</SafeAreaView>
);
}
Sonuca bakarsak AnaSayfamızda bulunan index kısmının Homepage olarak değiştiğini görebilirsiniz.
Şimdi aynı değişimi [username].js için yapalım ama bu sefer üst tarafta kullanıcı adının olmasını isteyelim.
//[username].js dosyamız
import { View, Text } from "react-native";
import React from "react";
import { Link, Stack, usePathname } from "expo-router";
import { StyleSheet } from "react-native";
import { SafeAreaView } from "react-native";
export default function UserPageWithSlug() {
let params = usePathname();
console.log(params);
return (
<SafeAreaView>
<Stack.Screen options={{ title: params?params.normalize():'UserPage' }} />
<View className=" h-full w-full justify-center align-middle items-center">
<Text className="text-lg font-bold border border-red-500 p-2">
<Text className="text-blue-500">UserName: </Text>
{JSON.stringify(params)}
</Text>
<Link
className="w-full bg-Primary text-white mt-4 p-3 text-center text-base border-1 border-black"
href={"/User"}
>
<Text>Go User</Text>
</Link>
<Link
className="w-full bg-Primary text-white mt-4 p-3 text-center text-base border-1 border-black"
href={"/"}
>
<Text>Go HomePage</Text>
</Link>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
mybutton: {
fontSize: 23,
padding: 3,
marginTop: 12,
backgroundColor: "#ffb500",
textAlign: "center",
borderRadius: 5,
},
});
Gördüğünüz için hedef sayanının içinde <Stack.Screen …/> oluşturduğumuz zaman header içinde sayfaya özel olan dataları ekrana basabiliyoruz.
2-)_layout.js içinde Stack.Screen tanımlamak.
Bu yöntemde ise index.js ve [username].js içinde bulunan dosyaları eski haline alalım ve _layout.js dosyamıza bu eklediğimiz <Stack.Screen … / >
konfigürasyonlarını ekleyelim.
import { Stack } from "expo-router";
export default () => {
return (
<Stack
screenOptions={{
headerStyle: {
backgroundColor: "blue",
},
headerTintColor: "white",
headerTitleStyle: {
fontWeight: "bold",
},
}}
>
<Stack.Screen options={{ title: "HomePage" }} />
<Stack.Screen
options={{ title: params ? params.normalize() : "UserPage" }}
/>
</Stack>
);
};
Şimdi burada bir hata ile karşılaşacağız , burada params değerinin ne olduğunu bilmiyor Expo bundan dolayı hata alıyoruz.
Bu ikinci yöntemin dezavantajı bu bu yöntem ile Header içine yada Stack’de herhangi bir alana o sayfaya özel değeleri malesef giremiyoruz.
(Bunun basit yada uygun bir yöntemi var mı açıkcası bilmiyorum şimdiye kadar Expo ile geliştirdiğim projelerde bu konfigürasyonu pek tercih etmedim ben .)
<Stack.Screen name="index" options={{ title: "HomePage" }} />
<Stack.Screen name="[username]" options={{ title: "UserPage" }} />
Hatayı düzeltmen için üst alanda bulunan Stack.Screen’leri bu değeler ile değiştirmemiz ve name değerini vermemiz gerekmekte,
Name değeri hedef dosyanın adı ile aynı olmalı
Modal
Modal yapısını kullanmak için projemizin kök dizinine bir adek modal.js adında bir dosya ekleyelim Daha sonra Stack Navigation içinde yeni bir Stack.Screen oluşturup name değerini modal yapalım. Burada önemli olan kodu option konfigürasyonları yaparken presentation:‘modal’ olarak ayarlamak olacaktır Modal olarak açılmasını sağlıyoruz bu sayede yeni bir sayfa olarak değil.
import { Stack } from "expo-router";
export default () => {
return (
<Stack
screenOptions={{
headerStyle: {
backgroundColor: "blue",
},
headerTintColor: "white",
headerTitleStyle: {
fontWeight: "bold",
},
}}
>
<Stack.Screen name="index" options={{ title: "HomePage" }} />
<Stack.Screen name="[username]" options={{ title: "UserPage" }} />
<Stack.Screen
name="modal"
options={{
// Set the presentation mode to modal for our modal route.
presentation: "modal",
}}
/>
</Stack>
);
};
Modal dosyamızın içine aşağıda eklediğim kodları ekleyelim, burada expo’nun ufak bir trick’inden bahsedeyim.
Gördüğünüz gibi href={“../”} olarak ayarlanmış bu bir önceki sayfaya geç demek.
index.js dosyamızın içine yeni bir alan ekleyelim.
<Link className="bg-blue-400 p-4 text-white rounded-xl" href={"/modal"}>
<Text>Open Modal</Text>
</Link>
Modal dosyamıza ekte bulunan kodları yapıştıralım.
//Modal dosyamız
import { View } from "react-native";
import { Link, useRouter } from "expo-router";
import { StatusBar } from "expo-status-bar";
import { Pressable } from "react-native";
import { Text } from "react-native";
export default function Modal() {
// If the page was reloaded or navigated to directly, then the modal should be presented as
// a full screen page. You may need to change the UI to account for this.
let router = useRouter();
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
{/* Use `../` as a simple way to navigate to the root. This is not analogous to "goBack". */}
<Link className="bg-blue-400 p-4 text-white rounded-xl" href={"../"}>
<Text>Close</Text>
</Link>
{/* Native modals have dark backgrounds on iOS, set the status bar to light content. */}
<StatusBar style="light" />
</View>
);
}
Tabs Navigation
Bir çok uygulamada hatta bir çok demek doğru olmaz en popüler navigastion tipidir. Ekranın en alt kısımda genelde icon ile beraber olan ve genelde 3 yada 4 elemandan oluşan bir menü tipidir. Twitter bu konu için en çok konuyu anlamamıza yardımcı olacak örnek olabilir.
Yukarıda görmüş olduğunuz görsel üzerinden anlatmak en anlaşılır ve en kolay yöntem olacak diye düşünüyorum.Gördüğünüz gibi AnaSayfa , Arama , Bildirim ve Mesaj olarak 4 adet Tab bulunuyor. Her tab ayrı bir sayfayı temsil ediyor ve bu tablere her tıklandığında mevcut sayfa artık o tabin temsil ettiği sayfaya routing edilmiş oluyor.
//_layout.js dosyamızı güncelleyelim.
import { Stack, Tabs } from "expo-router";
export default () => {
return (
<Tabs>
<Tabs.Screen name="index" options={{ title: "Home Page" }} />
<Tabs.Screen name="Search" options={{ title: "Search" }} />
</Tabs>
);
};
Bu değişiklikten sonra hemen [username].js isimli dosyamızın içine hemen <Tabs.Scree… /> satırını ekleyelim.
import { View, Text } from "react-native";
import React from "react";
import { Link, Tabs, usePathname } from "expo-router";
import { StyleSheet } from "react-native";
import { SafeAreaView } from "react-native";
export default function UserPageWithSlug() {
let params = usePathname();
return (
<SafeAreaView>
<Tabs.Screen options={{ title: params }} />
<View className=" h-full w-full justify-center align-middle items-center">
<Text className="text-lg font-bold border border-red-500 p-2">
<Text className="text-blue-500">UserName: </Text>
{JSON.stringify(params)}
</Text>
<Link
className="w-full bg-Primary text-white mt-4 p-3 text-center text-base border-1 border-black"
href={"/User"}
>
<Text>Go User</Text>
</Link>
<Link
className="w-full bg-Primary text-white mt-4 p-3 text-center text-base border-1 border-black"
href={"/"}
>
<Text>Go HomePage</Text>
</Link>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
mybutton: {
fontSize: 23,
padding: 3,
marginTop: 12,
backgroundColor: "#ffb500",
textAlign: "center",
borderRadius: 5,
},
});
Bu sayede Tabs ekranınıda bu kadar basit bir şekilde çözmüş olduk.
Peki iç içe routing yapısını nasıl oluştururuz ?
Şimdi projemiz içinde olan bütün dosyaları silelim .
Kök dizinimizde index.js ve Register.js olmak üzere 2 adet dosya oluşturalım
//index.js dosyamız.
import { Link, useRouter } from "expo-router";
import { Pressable, SafeAreaView } from "react-native";
import { StyleSheet, Text, View } from "react-native";
export default function Page() {
let router = useRouter();
const handlePress = () => {
router.replace("Home");
};
return (
<SafeAreaView>
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center",
gap: 12,
}}
>
<Pressable onPress={handlePress}>
<Text>Login</Text>
</Pressable>
<Link href={"/Register"} asChild>
<Pressable>
<Text>Create Account</Text>
</Pressable>
</Link>
</View>
</SafeAreaView>
);
}
//register.js dosyamız.
import { View, Text } from 'react-native'
import React from 'react'
const Register = () => {
return (
<View>
<Text>Register</Text>
</View>
)
}
export default Register
Daha sonra bir klasör oluşturalım (tabs) adını verelim.
Daha sonra tabs klasörümüzün içine Home.js ve Profile.js isimli iki dosya oluşturalım.
//Home.js
import { View, Text } from "react-native";
import React from "react";
import { SafeAreaView } from "react-native";
const home = () => {
return (
<SafeAreaView>
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center",
gap: 12,
}}
>
<Text className="">HomePage</Text>
</View>
</SafeAreaView>
);
};
export default home;
//Profile.js
import { View, Text, Pressable } from "react-native";
import React, { useEffect } from "react";
import { SafeAreaView } from "react-native";
import { Link, useRouter } from "expo-router";
const Profile = () => {
let router = useRouter();
return (
<SafeAreaView>
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center",
gap: 12,
}}
>
<Text>Profile</Text>
<Pressable onPress={() => router.push("/")}>
<Text>LogOut</Text>
</Pressable>
</View>
</SafeAreaView>
);
};
export default Profile;
Ve bir adet _layouth.js dosyası oluşturalım.
import { Stack } from "expo-router";
export default () => {
return (
<Stack>
<Stack.Screen name="index" options={{ headerShown: true }} />
<Stack.Screen name="(tabs)" options={{ headerShown: true }} />
</Stack>
);
};
Şimdi Login kısmına tıklayınca /HomePage URL adresine yönlendiriyoruz.
Gördüğünüz gibi burada Login butonuna tıklıyoruz ve (tabs) altında bulunan Home sayfasına yönlendiriliyoruz.
Bu (tabs) klasörünün altında bulunan bütün sayfaların Tabs olarak ayarlanmasını isteyelim yapmamız gereken neydi _layout.js dosyası oluşturmak hemen oluşturalım.
// (tabs)/_layouth.js dosyamız
import { Tabs } from "expo-router";
import { AntDesign } from "@expo/vector-icons";
import { Feather } from "@expo/vector-icons";
export default () => {
return (
<Tabs>
<Tabs.Screen
name="Home"
options={{
tabBarLabel: "HomePage",
headerTitle: "Home Page",
tabBarIcon: ({ color, size }) => (
<AntDesign name="home" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="List"
options={{
tabBarLabel: "List",
headerTitle: "List",
tabBarIcon: ({ color, size }) => (
<Feather name="list" size={size} color={color} />
),
headerShown:false
}}
/>
<Tabs.Screen
name="Profile"
options={{
tabBarLabel: "Profile",
headerTitle: "Profile",
tabBarIcon: ({ color, size }) => (
<AntDesign name="home" size={size} color={color} />
),
}}
/>
</Tabs>
);
};
Burada bir sorun var gördüğünüz gibi hem Stack hem Tabs’in Header alanı oluşmuş bunu engellemek için ilk olarak.
Kök klasörümüzün altında bulunan _layout.js dosyamızı düzenlememiz gerekiyor.
import { Stack } from "expo-router";
export default () => {
return (
<Stack>
<Stack.Screen name="index" options={{ headerShown: false }} />
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
);
};
Gördüğünüz gibi Stack’in Header’ını gizledik.
Şimdi yeni bir klasör ekleyelim (tabs)’in altına
ismine List diyelim. İçine iki adet dosya oluşturalım.
[id].js ve index.js olarak isimlendirelim bu dosyaları.
//İd.js dosyamız
import { View, Text } from "react-native";
import React from "react";
import { SafeAreaView } from "react-native";
import {useRouter, useSearchParams } from "expo-router";
const SingleItem = () => {
let params = useSearchParams();
return (
<>
<SafeAreaView>
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center",
gap: 12,
}}
>
<Text>SingleItem</Text>
<Text>{JSON.stringify(params.id)}</Text>
</View>
</SafeAreaView>
</>
);
};
export default SingleItem;
//index.js dosyamız
import { View, Text } from "react-native";
import React from "react";
import { SafeAreaView } from "react-native";
import { Link } from "expo-router";
const ItemList = () => {
return (
<SafeAreaView>
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center",
gap: 12,
}}
>
<Link href="/List/1">News One</Link>
<Link href="/List/2">News Two</Link>
<Link href="/List/3">News Three</Link>
</View>
</SafeAreaView>
);
};
export default ItemList;
Burada gördüğünüz gibi List ve List/id olarak iki adet Tab oluştu bu bir problem bunu nasıl engelleriz.
List klasörümüzün altında hemen bir _layout.js dosyası oluşturalım.
import { Stack } from "expo-router";
export default () => {
return (
<Stack>
<Stack.Screen name="index" options={{ headerTitle: "ItemList" ,headerBackTitleVisible:false}} />
</Stack>
);
};
Gördüğünüz gibi Tab alanlarımız istediğimiz gibi şekillendi. Projemizin son hali ;
Elimden geldiği kadarıyla konuyu anlatmak istedim. Hatam kusurum yazım yanlışım olduysa affola.

Arkadaşlar öncelikle bu notlar kendi kendime konuyu öğrenmek için ve daha sonra bir şeyde takılırsam dönüp bakmak için almış olduğum notlar yazım hatalarım vs olabilir lütfen dikkate almayın.
Ekranlar arasında gezinme
Geliştirdiğimiz yazılımın mobil bir uygulama olarak değil bir web projesi olsaydı, şöyle bir şey yazabilirdik:
<a href="details.html">Go to Details</a>
<a onClick={() => {window.location.href = 'details.html';}}>
Go to Details
</a>
Normal şartlarda React için ise props yollarken ek olarak navigation isimli bir değişkende yollanmış oluyor React Native kullanırken ise props yardımı ile sayfalar arası geçiş yapabiliyoruz.
Örnek olarak.
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
Yukarıda bir View içine bir adet button eklendiğini eklenen bu butona tıklandığı zaman detay sayfasına geçişin nasıl yapıldığı gösterilmiş. Bu sayfalama sistemi ise Navigation tipine göre Screenler ile belirleniyor.
Önemli :
- navigation.navigate(‘HomeDetails’) => çalışma mantığı şu sayfaya git ama o eğer ben o sayadaysam hiçbir şey yapma !
function Home({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('HomeDetails')}
/>
</View>
);
}
function HomeDetails({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('HomeDetails')}
/>
</View>
);
}
Burada konuyu söyle hikayelendirelim.
Yukarıda eklemiş olduğum örneği inceleyelim .
Home diye bir sayfadayız bu sayfanının içinde bir adet buton var ve bu Buton’a tıkladığımız zaman HomeDetails isimli bir sayfaya gönderiyor bizi.
Bu home Details’in içinde aynı zamanda tekrar bir buton var ve bu buton bizim için tekrardan HomeDetails sayfasına göndermek için ayarlanmış.
Simdi Home ekranında butona bastığımız zaman HomeDetails sayfasına geçiş yaptığımızı düşünelim .
HomeDetails sayfasında tekrar butona basarsak ne olur ?
Hiçbir şey !
- navigation.push(‘HomeDetails’) => Burada bulunan push methodu ilerleyen konularda bunu daha iyi anlayacağız . Detay sayfasında isek bile yeniden Detay sayfasına erişmemizi sağlıyor. Genelde push methodu kullanılırken parametreler ile beraber datalarda gönderiliyor.
<Button
title="Go to Details... again"
onPress={() => navigation.push('Details')}
/>
Döküman içinde çok basit bir analtım yapmışlar aslında bu konu ile iligi push her çağrıldığında stack içine yeni bir route eklendiğini daha önce bu rota oluşturulmuş mu kontrol bile edilmediği söyleniyor.
Navigate her çağrıldığında ise daha önce stack içinde bu route varmı kontrol edip yoksa stack içine eklendiği söyleniyor.
- navigation.goBack() => bir önceki sayfaya dönmek için kullanılıyor.
- navigation.popToTop() => Direkt stack içide olan ilk sayfaya dönüyor.navigasyon
Stack Navigator kullanıyorsanız
replace— mevcut rotayı yenisiyle değiştirin
push— yığına yeni bir rota itin
pop— yığına geri dön
popToTop— yığının en üstüne git
Tab Navigator yapısı kullanıyorsanız:
jumpTo— sekme gezgininde belirli bir ekrana git
Drawer için ise, aşağıdakiler de kullanılabilir:
jumpTo— çekmece gezgininde belirli bir ekrana git
openDrawer— çekmeceyi aç
closeDrawer— çekmeceyi kapat
toggleDrawer— durumu değiştir, yani. kapalıdan açık duruma geçiş ve tam tersi
Router kullanarak parametre geçme.
navigation.navigate(‘RouteName’, { /* params go here */ }
Route yaparken eğer parametre’de eklemek istersek yukarıda görmüş olduğunuz gibi yapı kullanabiliriz. ve bu yolladığımız değerlere erişmek için ise route.params fonksiyonunu kullanabiliriz.
Şimdi bir örnek üzerinden inceleyelim bu konuyu öncelikle bizim 2 adet objemiz olsun.
//Bu Stack'in ilk elamanından Detay sayfasına gidecek olan obje
let FirstObj= {
itemId: 86,
otherParam: {name: 'Kubilay'},
}
//Detay sayfasından diğer detay sayfalarına gidecek olan obje
let SecondObj = {
itemId: Math.floor(Math.random() * 100),
otherParam: {name: 'Ali' + Math.floor(Math.random() * 100)},
};
Şimdi iki adet Sayfa tasarlayalım birinci sayfamız HomeScreen ve diğer sayfamız ise Detay sayfası ;
function HomeScreen({navigation}) {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate('Details', FirstObj);
}}
/>
</View>
);
}
function DetailsScreen({route, navigation}) {
/* 2. Get the param */
const {itemId, otherParam} = route.params;
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Details Screen</Text>
<Text>itemId: {JSON.stringify(itemId)}</Text>
<Text>otherParam: {JSON.stringify(otherParam)}</Text>
<Button
title="Go to Details... again"
onPress={() => navigation.push('Details', {
itemId: Math.floor(Math.random() * 100),
otherParam: {name: 'Ali' + Math.floor(Math.random() * 100)},)}
/>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
<Button title="Go back" onPress={() => navigation.goBack()} />
</View>
);
}
const Stack = createNativeStackNavigator();
//Stack navigation
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Gördüğünüz gibi Detay sayfasına navigation ile beraber data yollamak için önce gidilecek olan sayfanın adını tanımladık daha sonra gönderilecek olan değerleri tanımladık.
navigation.navigate('Details', FirstObj);
Şimdi önce sonuca bakalım. İki farklı sonuç göstermek istiyorum birincisi navigation.push ile yapılmış bir diğeri ise navigation.navigate olarak geliştirilmiştir burada bir önceki konumuz olan fonksiyonlarda bahsettiğim konuyu aşağıda olan görseller ile daha net anlayabilirsiniz.
Şimdiye kadar bahsettiklerimiz tamamen en basit özellikleriyle parametre göndermek ile ilgiliydi şimdi biraz daha derinlere inelim bu konu hakkında.
Updating params(parametreleri update etmek)
navigation.setParams({
query: ‘someText’,
});
Yukarıda en basit şekilde kullanımından bahsedilen setParams fonksiyonu o an aktif olan sayfanın parametre değerlerini değiştirmemize yardımcı olmaktadır.
Örnek olarak
function DetailsScreen({route, navigation}) {
/* 2. Get the param */
const {itemId, otherParam} = route.params;
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Details Screen</Text>
<Text>itemId: {JSON.stringify(itemId)}</Text>
<Text>otherParam: {JSON.stringify(otherParam)}</Text>
<Button
title="Go to Details... again"
onPress={() =>
navigation.navigate('Details', {
itemId: Math.floor(Math.random() * 100),
otherParam: {name: 'Ali' + Math.floor(Math.random() * 100)},
})
}
/>
<Button title="Go to Home" onPress={() => navigation.push('Home')} />
<Button title="Go back" onPress={() => navigation.goBack()} />
<Button
title="Update"
onPress={() =>
navigation.setParams({
otherParam: {name: 'BzkStore' + Math.floor(Math.random() * 100)},
})
}
/>
</View>
);
}
Burada push yada navigate kullanmak hiçbir fark oluşturmuyor sadece o an aktif olan sayfada bulunan değerleri değiştirmek için kullanılan fonksiyon.
Daha Fazlası için : https://reactnavigation.org/docs/navigation-prop/#setparams
Initial params
İnitial valuları atamak için ,değerler Screen altından gönderilir.
<Stack.Screen
name=”Details”
component={DetailsScreen}
initialParams={{ itemId: 42 }}
/>
Bir önceki ekrana değer yollama
ReactNavigation kullanarak bir önceki ekrana değer yollayabiliriz aslında bunu bir web sayfasında Modal kullanarak veri alma gibi düşünebilirsiniz eğer kullanıcıdan bir veri almanız gerekiyor ise ilk tercihiniz genelde bir modal açıp state ile bu veriyi kullanıcıdan alarak ekrana yazdırmak ilk tercihlerimiz arasında olur. Fikir aslında tamamen aynı fakat burada modal değil ekran yapısı kullanacağız.
function HomeScreen({ navigation, route }) {
React.useEffect(() => {
if (route.params?.post) {
// Post updated, do something with `route.params.post`
// For example, send the post to the server
}
}, [route.params?.post]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Veri Oluştur"
onPress={() => navigation.navigate('CreatePost')}
/>
<Text style={{ margin: 10 }}>Veri: {route.params?.post}</Text>
</View>
);
}
function CreatePostScreen({ navigation, route }) {
const [postText, setPostText] = React.useState('');
return (
<>
<TextInput
multiline
placeholder="Gönderilecek veri ne olsun?"
style={{ height: 200, padding: 10, backgroundColor: 'white' }}
value={postText}
onChangeText={setPostText}
/>
<Button
title="Veriyi Yolla"
onPress={() => {
navigation.navigate({
name: 'Home',
params: { post: postText },
merge: true,
});
}}
/>
</>
);
}
Yukarıda görmüş olduğunuz iki adet comp. için söyle bir senaryo düşünelim . Veri oluştur düğmesi olan bir ekranınız olduğunu varsayalım ve Veri oluştur düğmesi kullanıcıdan veri almak için uygulamamızı yeni bir ekrana route ettirdiğimizi düşünelim . Gönderiyi oluşturduktan sonra, gönderiye ilişkin verileri önceki ekrana geri geçirmek isteyelim.
Burada şöyle ufak bir trick kullanacağız GoBack fonksiyonu gibi çalışan bir Navigate fonksiyonu kullanacağız bu sayede veriyi geriye göndermiş olabileceğiz.
Bir sonraki yazımda ReactNavigation ile ilgili daha fazla detaya inerek elimden geldiği kadarıyla konuları anlatmaya çalışacağım tekrar söylemek istiyorum içerikler önceden kendi kendime aldığım notları paylaşmak amacı ile oluşturuyorum bundan dolayı yazım yanlışlarım vs olabilir lütfen mazur görün.

Bir çok uygulamada hatta bir çok demek doğru olmaz en popüler navigastion tipidir. Ekranın en alt kısımda genelde icon ile beraber olan ve genelde 3 yada 4 elemandan oluşan bir menü tipidir. Twitter bu konu için en çok konuyu anlamamıza yardımcı olacak örnek olabilir.
Yukarıda görmüş olduğunuz görsel üzerinden anlatmak en anlaşılır ve en kolay yöntem olacak diye düşünüyorum.Gördüğünüz gibi AnaSayfa , Arama , Bildirim ve Mesaj olarak 4 adet Tab bulunuyor. Her tab ayrı bir sayfayı temsil ediyor ve bu tablere her tıklandığında mevcut sayfa artık o tabin temsil ettiği sayfaya routing edilmiş oluyor.
Şimdi beraber TabNavigator kullanarak basit bir uygulama geliştirelim.
TabNav. kullanabilmek için öncelikle projemize aşağıda bulunan paketi yüklememiz gerekmekte.
npm install @react-navigation/bottom-tabs
Paketi yükledikten sonra projeyi yeniden başlatmanızı tavsiye ederim ek olarak android tarafında çalışmalar yaptığım için ben ek bir ayar yapmama gerek yok fakat ios tarafında çalışıyorsanız ufak bir ayarlama yapmanız gerekecek ayarlama denemez aslında uygulamanın sağlıklı çalışabilmesi için native Cocoapods kütüphanelerini kurmanız gerekli.
Kök dizinde iken aşağıda bulunan kodları uygulamanız yeterli.
cd ios
pod install
cd ..
Şimdi aşağıda bulunan gist üzerinden anlatımıma devam edeyim.
Öncelikle burada bulunan bütün kodları kopyalayıp App.js içine yapıştıralım ve tek tek beraber kodları inceleyelim.
Öncelikle Navigator yapısını kullanmamız için import etmemiz gereken kütüphane ve fonksiyon var.(Satır 3 ve Satır 4)
Yukarıda isimlerinden de anlayacağınız üzere Bir adet Container ve bir adet fonksiyonumuz var. NavigationContainer’i bir provider olarak düşünebilirsiniz. createBottomTabNavigator ise bir fonksiyon bu fonksiyon sayesinde bir navigation işlemlerimizi yapabilicez.
Öncelikle import etmiş olduğumuz createBottomTabNavigator’ı bir değişkene atayalım bu sayede erişebilelim.
const Tab = createBottomTabNavigator();
Daha sonra App Componentimizi import etmiş olduğumuz Provider ile saralım.
function App(){return (<NavigationContainer> ... </NavigationContainer>);}
Şimdi genel olarak bütün Navigation işlemlerinde bunu yapacağız daha sonra kullanmak istediğimiz Navigation yöntemini belirtmemiz gerekiyor bir BottomTabNavigation kullanıyoruz o zaman;
function App(){return (<NavigationContainer>
<Tab.Navigator>
...........
</Tab.Navigator>
</NavigationContainer>);}
Şeklinde bir tanımlama yapmamız gerekiyor.
(Burada Tab bizim fonksiyona erişmek için atadığımız değişkenin adı isterseniz o değişkenin adını Router olarak atayın isterseniz adınız olarak atayın hiç fark etmez onun sayesinde istediğimiz yapılara erişebiliyoruz.)
Router yapımızı oluşturmak için ise Screen fonksiyonunu kullanmamız gerekiyor.
Screen Fonksiyonu 2 main api ihtiyacı duyar bunlar Name ve Component
Name : Dışarıdan navigation’a erişebilmemiz için gerekli olan takma isim diyebiliriz.
Component : O sayfada gösterilecek olan component.
https://reactnavigation.org/docs/screen (Daha açıklayıcı tanımlamalar ve daha fazla özelleştirilebilir api için mutlaka göz atın)
Basitçe yapısı ;
<Tab.Screen name="Home" component={HomeScreen} />
Şimdi Bütün Router yapımızı tanımladığımız hale bi göz gezdirelim.
Gördüğünüz gibi hepsi tek bir BottomTabNavigator içinde ve birbirleri ile ilişkili bir durumda çalışıyor yani biz buradan istediğimiz şekilde Route değişikliği yapabiliriz.
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
Uygulamamızı çalıştıralım;

Nedir bu react native navigator ve neden ihtiyacımız var böyle bir şeye ?
Bir web tarayıcısında, bağlantı (<a>) etiketi kullanarak farklı sayfalara bağlantı verebiliyorduk. Kullanıcı bir linke tıkladığında o an aktif olan URL tarayıcı geçmişi yığınına gönderiliyor ve yeni sayfa kullanıcının karşısına açılıyordu. Eğer kullanıcı geri düğmesine bastıysa tarayıcı öğeyi geçmiş yığınından alıp kullanıcı karşısına getiriyordu böylece etkin sayfa artık daha önce ziyaret edilen sayfa olmuş oluyordu. Aslında basitçe düşünürsek tamamen aynı mantık yani web sitesi içerisinde dolaşmamış için gerekli olan linklerin mobil tarafta kullanılması için gerekli olan bir deneyim.
Native stack navigator
Yukarıda görmüş olduğunuz görsel üzerinden anlatmak en anlaşılır ve en kolay yöntem olacak diye düşünüyorum. Stack Navigator’ün amacı, bir ekrandan (sayfadan yada link olarak da düşünebilirsiniz ) diğer ekrana geçiş için yeni ekranları çağırarak stack içine eklenmesini sağlamaktır. Yukarıda konuya değinirken tarayıcı örneği vermiştim aslında basitçe düşürsek çalışma mantıkları birebir aynı burada da sol üst kısımda gördüğünüz geri butonları ile geri gidebilirsiniz ve ekranınıza o an bir önceki sayfa gelir.
Şimdi beraber StackNavigator kullanarak basit bir uygulama geliştirelim.
StackNav. kullanabilmek için öncelikle projemize aşağıda bulunan paketi yüklememiz gerekmekte.
npm install @react-navigation/native-stack
Paketi yükledikten sonra projeyi yeniden başlatmanızı tavsiye ederim ek olarak android tarafında çalışmalar yaptığım için ben ek bir ayar yapmama gerek yok fakat ios tarafında çalışıyorsanız ufak bir ayarlama yapmanız gerekecek ayarlama denemez aslında uygulamanın sağlıklı çalışabilmesi için native Cocoapods kütüphanelerini kurmanız gerekli.
Kök dizinde iken aşağıda bulunan kodları uygulamanız yeterli.
cd ios
pod install
cd ..
Şimdi aşağıda bulunan gist üzerinden anlatımıma devam edeyim.
Öncelikle burada bulunan bütün kodları kopyalayıp App.js içine yapıştıralım ve tek tek beraber kodları inceleyelim.
Öncelikle Navigator yapısını kullanmamız için import etmemiz gereken kütüğhane ve fonksiyon var.(Satır 3 ve Satır 4)
Yukarıda isimlerinden de anlayacağınız üzere Bir adet Container ve bir adet fonksiyonumuz var. NavigationContainer’i bir provider olarak düşünebilirsiniz. createNativeStackNavigator ise bir fonksiyon bu fonksiyon sayesinde bir navigation işlemlerimizi yapabilicez.
Öncelikle import etmiş olduğumuz createNativeStackNavigator’ı bir değişkene atayalım bu sayede erişebilelim.
const Stack = createNativeStackNavigator();
Daha sonra App Componentimizi import etmiş olduğumuz Provider ile saralım.
function App(){return (<NavigationContainer> ... </NavigationContainer>);}
Şimdi genel olarak bütün Navigation işlemlerinde bunu yapacağız daha sonra kullanmak istediğimiz Navigation yöntemini belirtmemiz gerekiyor bir StackNavigation kullanıyoruz o zaman;
function App(){return (<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
...........
</Stack.Navigator>
</NavigationContainer>
);}
Şeklinde bir tanımlama yapmamız gerekiyor.
(Burada Stack bizim fonksiyona erişmek için atadığımız değişkenin adı isterseniz o değişkenin adını Router olarak atayın isterseniz adınız olarak atayın hiç fark etmez onun sayesinde istediğimiz yapılara erişebiliyoruz.)
Tanımlamayı yaptık ( sarmalladık diyebiliriz aslında) daha sonra uygulama açıldığı zaman başlayacak olarak yani kullanıcının karşısına çıkacak olan ilk sayfayı belirtmek için mutlala initialRouteName tanımlamasını yapmamız gerekiyor.
Router yapımızı oluşturmak için ise Screen fonksiyonunu kullanmamız gerekiyor.
Screen Fonksiyonu 2 main api ihtiyacı duyar bunlar Name ve Component
Name : Dışarıdan navigation’a erişebilmemiz için gerekli olan takma isim diyebiliriz.
Component : O sayfada gösterilecek olan component.
https://reactnavigation.org/docs/screen (Daha doğru tanımlamalar ve daha fazla api için mutlaka göz atın)
Basitçe yapısı ;
<Stack.Screen name="Home" component={HomeScreen} />
Şimdi Bütün Router yapımızı tanımladığımız hale bi göz gezdirelim.
Gördüğünüz gibi hepsi tek bir Stack içinde ve birbirleri ile ilişkili bir durumda çalışıyor yani biz buradan istediğimiz şekilde Route değişikliği yapabiliriz.
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="SignUp" component={SignUp} />
<Stack.Screen name="ResetPassword" component={ResetPassword} />
<Stack.Screen name="Details" component={DetailsScreen} /> </Stack.Navigator>
</NavigationContainer>
Uygulamamızı çalıştıralım
Hiç tanımlama yapmamış olmamıza rağmen üst tarafta bir Header oluştu gördüğünüz gibi bu o an hangi sayfada olduğumuzu söylüyor bize.
Peki biz burada bulunan Giriş Yap Yada Kayıt Ol butonuna tıkladığımız zaman nasıl o routerlara erişeceğiz.
Bildiğiniz gibi bir React Componenti Props alırken aynı zamanda navigation adında bir objede almakta bunu çağırarak bu Route işlemlerini yapabiliriz.
Örnek olarak Home Componentini ele alalım
function HomeScreen({navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{fontSize:18,marginBottom:12}}>KubilayBzk.dev 'e hoşgeldiniz</Text>
<View style={{display:'flex', flexDirection:"row",justifyContent:"space-around"}}>
<Button onPress={()=>navigation.navigate("Login")} title="Giriş Yap"/>
<Button onPress={()=>navigation.navigate("SignUp")} title="Kayit Ol"/>
</View>
</View>
);}
Görüğünüz gibi props olarak gelen objenin navigate fonksiyonuna içinde olan navigate içine App comp. içinde belirtmiş olduğumuz name değerlerini girdik .
Uygulamamızın görüntüsü

Pre-rendering
Varsayılan olarak, Next.js her sayfayı önceden işler . Bu, Next.js’nin istemci tarafı JavaScript tarafından yapılması yerine her sayfa için önceden HTML oluşturduğu anlamına gelir. Ön işleme, daha iyi performans ve SEO ile sonuçlanabilir.
Oluşturulan her HTML, o sayfa için gerekli olan minimum JavaScript koduyla ilişkilendirilir. Bir sayfa tarayıcı tarafından yüklendiğinde, JavaScript kodu çalışır ve sayfayı tamamen etkileşimli hale getirir. (Bu işleme hidrasyon denir .)
Two forms of Pre-rendering
Next.js’nin iki ön işleme biçimi vardır: Statik Oluşturma ve Sunucu Tarafı İşleme . Fark, bir sayfa için HTML oluşturduğundadır.
- Statik Oluşturma Static Generation (Önerilen) : HTML, derleme zamanında oluşturulur ve her istekte yeniden kullanılır.
- Sunucu Tarafı Oluşturma — Server-side Rendering : HTML, her istekte oluşturulur .
Next.js , her sayfa için kullanmak istediğiniz ön işleme formunu seçmenize izin verir. Çoğu sayfa için Statik Oluşturma’yı ve diğerleri için Sunucu Tarafı İşleme’yi kullanarak bir “karma” Next.js uygulaması oluşturabilirsiniz.
Performans nedenleriyle Sunucu Tarafı Oluşturma yerine Statik Üretimi kullanmanızı öneririz . Statik olarak oluşturulmuş sayfalar, performansı artırmak için ekstra yapılandırma olmadan CDN tarafından önbelleğe alınabilir. Ancak bazı durumlarda Sunucu Tarafı Oluşturma tek seçenek olabilir. [Content Delivery Network (CDN) İçerik dağıtım ağı anlamına gelir]
Statik Üretim veya Sunucu Client-side Rendering (Tarafı İşleme ile birlikte İstemci Tarafı İşleme’yi) de kullanabilirsiniz . Bu, bir sayfanın bazı bölümlerinin tamamen istemci tarafı JavaScript tarafından oluşturulabileceği anlamına gelir.
Static Generation (Önerilen)
Bir sayfa Statik Üretim kullanıyorsa , sayfa HTML’si build time’da oluşturulur . Bu, next build,komutu çalıştırdığında sayfa HTML’sinin oluşturulduğu anlamına gelir . Bu HTML daha sonra her istekte yeniden kullanılacaktır. Bir CDN tarafından önbelleğe alınabilir
Static Generation with data
Bazı sayfalar, ön işleme için harici verilerin alınmasını gerektirir. İki senaryo vardır ve biri veya her ikisi de geçerli olabilir. Her durumda, Next.js’nin sağladığı şu işlevleri kullanabilirsiniz
- Sayfa içeriğiniz (content) harici verilere bağlıdır: getStaticProps.
- Sayfa içeriğiniz (content) path ile verilere bağlıdır getStaticPaths ve getStaticProps
Senaryo 1 : Sayfa içeriğiniz (content) harici verilere bağlıdır: getStaticProps.
Elimizde bir blog içeriği yönetme uygulaması olduğunu düşünelim. Ve ekteki gibi bir comp olsun .
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
Pre-render aşamasında veriyi çekmek için NextJS async olan ve export edilmesi gereken bir fonksiyon kullanmamızı bekler.
// This function gets called at build time
export async function getStaticProps() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
export default Blog
Senaryo 2 Sayfa içeriğiniz (content) path ile verilere bağlıdır.
Next.js, dinamik rotalara sahip sayfalar oluşturmanıza olanak tanır . Örneğin, pages/posts/[id].js gibi gibi
Dışarıdan gelecek olan veri bu id değerine bağlı olarak değişkenlik gösterecektir.
Elimizde www.kubilaybozak.com/posts/1 adında bir sayfamız olduğunu varsayalım bu sayfa için sadece linke tıklandığı zaman pre render olmasıını isteyelim.
Bu tarz durumlarda bizim sayfalarımız veriye bağlı olarak render edilir.
Bu gibi durumlar için Next.js, dinamik bir sayfadan (bahsedilen [post].js ) çağrılan async ve export edilmesi gereken getStaticPath isimli bir fonksiyon kullanır.
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
Yukarıda öncelikle postları çektik daha sonra map ile id’lere göre sayfalar oluşturmamız gerekiyor .
function Post({ post }) {
// Render post...
}
export async function getStaticPaths() {
// ...
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return { props: { post } }
}
export default Post
Daha sonra bu konuların detayına gireceğiz şimdi sıra Server Side Rendering kısmında.
Server Side Rendering
Bir sayfa Server-side Rendering, her istekte sayfa HTML’si oluşturulur .
Bir sayfa için Server-side Rendering kullanmak için adlı exportedilen asyncbir fonksiyon olan getServerSidePropfoksiyonuna ihtiyacınız vardır .Bu işlev, her istekte sunucu tarafından çağrılır.
Örneğin, sayfanızın sık güncellenen verileri (harici bir API’den alınan) önceden oluşturması gerektiğini varsayalım.
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
Gördüğünüz gibi, getServerSideProps'e benzer getStaticProps, ancak fark, getServerSidePropsderleme zamanı yerine her istekte çalıştırılmasıdır.
/////////////////////////////////////////////////////////////////////
Server -Side-Rendering getServerSideProps(SSR)
Eğer bir sayfa sunucu tarafında oluşturulmak isteniyorsa kullanılması gereken fonksiyon.Next.js tarafından döndürülen verileri kullanarak her istekte bu sayfayı önceden oluşturur ve tarayıcı tarafına datalar ile beraber .
- getServerSidePropsyalnızca sunucu tarafında çalışır ve hiçbir zaman tarayıcıda çalışmaz.
- Bir sayfayı doğrudan request yollandığı zaman getServerSideProps istek zamanında çalışır ve return edilen propslara göre sayfa oluşturulur.
- sadece page klasörü altında bulunan .js .jsx .tsx gibi dosyalarda kullanılabilir .
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
Parametre olarak verilen context, aşağıdaki anahtarları içeren bir nesnedir:
- params: Bu sayfa dinamik bir rota kullanıyorsa , paramsrota parametrelerini içerir. Sayfa adı ise [id].js, paramsşöyle görünecektir { id: ... }
- query: Dinamik yol parametreleri dahil, sorgu dizesini temsil eden bir nesne.
- localeetkin yerel ayarı içerir (etkinleştirilmişse).
- localesdesteklenen tüm yerel ayarları içerir (etkinleştirilmişse).
- defaultLocaleyapılandırılmış varsayılan yerel ayarı içerir (etkinleştirilmişse).
Return etmesi gereken değerler .
Mutlaka bir değer return etmesi gerekir genelde bu değerler dinamik olan değerler olur yukarıda bahsettiğimiz [1.js, 2.js 3.js gibi gibi ]
- props, her değerin sayfa bileşeni tarafından alındığı bir anahtar/değer çiftidir mesela id vb. gibi datlar .
export async function getServerSideProps({ params }) {
const resp = await fetch `https://Test.com/pokemon/${params.id}.json`
);
if (!pokemon) {
return null; }
return {
props: {
pokemon: await resp.json(),
},
};}
- notFound ,Sayfanın olmaması bulunanmaması yada fetch işlemine çıkan bir hata sonucu 404 SayfanotFound döndürmesine izin verir .
export async function getServerSideProps(context) {
const res = await fetch(`https://.../data`)
const data = await res.json()
if (!data) {
return {
notFound: true,
}
}
return {
props: { data }, // will be passed to the page component as props
}
}
- redirect, internal ve external kaynaklara yeniden yönlendirmeye izin verir. şekline uygun olmalıdır { destination: string, permanent: boolean }. HTTPBazı ender durumlarda, eski istemcilerin doğru şekilde yeniden yönlendirme yapması için özel bir durum kodu atamanız gerekebilir . Bu durumlarda, statusCodemülk yerine permanentmülkü kullanabilirsiniz, ancak ikisini birden kullanamazsınız.
getServerSideProps’u ne zaman kullanmalıyım?
Yalnızca, verileri istek zamanında alınması gereken bir sayfa oluşturmanız gerekiyorsa kullanmalısınız.
Basit bir örnek;
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default P
- ****************************************************************
Static -Side-Generation getStaticProps (SSG)
Statik olarak oluşturduğumuz sayfaların derleme sırasında otomatik olarak oluşmasını istediğimiz zaman kullandığımı bir teknik.
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
- paramsdinamik rotalar kullanan sayfalar için rota parametrelerini içerir . Örneğin, sayfa adı ise [id].jsşöyle paramsgörünecektir { id: ... }. getStaticPathsBuradan gelen bilgileri bu fonksiyonu bir sonraki konuda bahsedeceğiz.
- previewtruesayfanın Önizleme Modunda olup olmadığı ve undefinedaksidir.
- previewDatatarafından ayarlanan önizleme verilerini içerir setPreviewData.
- localeetkin yerel ayarı içerir (etkinleştirilmişse).
- localesdesteklenen tüm yerel ayarları içerir (etkinleştirilmişse).
- defaultLocaleyapılandırılmış varsayılan yerel ayarı içerir (etkinleştirilmişse).
getStaticProps return değerleri
- props, her değerin sayfa bileşeni tarafından alındığı bir anahtar/değer çiftidir.
export async function getStaticProps({ params }) {
const resp = await fetch(`test.com/pokemon/${params.id}.json`);
return {
props: {
pokemon: await resp.json()
},};}
- revalidate, sayfanın yeniden oluşturulmasının gerçekleşebileceği saniye cinsinden miktardır (varsayılanı falseyeniden doğrulamadır veya yeniden doğrulama yoktur).
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
- notFound ,Sayfanın olmaması bulunanmaması yada fetch işlemine çıkan bir hata sonucu 404 SayfanotFound döndürmesine izin verir .
export async function getStaticProps(context) {
const res = await fetch(`https://.../data`)
const data = await res.json()
if (!data) {
return {
notFound: true,
}
}
return {
props: { data }, // will be passed to the page component as props
}
}
- redirect, internal ve external kaynaklara yeniden yönlendirmeye izin verir. şekline uygun olmalıdır { destination: string, permanent: boolean }. HTTPBazı ender durumlarda, eski istemcilerin doğru şekilde yeniden yönlendirme yapması için özel bir durum kodu atamanız gerekebilir . Bu durumlarda, statusCodemülk yerine permanentmülkü kullanabilirsiniz, ancak ikisini birden kullanamazsınız.
getStaticPaths
Bir sayfanın Dinamik Rotaları (pokemon/[id].js gibi ) varsa ve kullanıyorsa getStaticProps, statik olarak oluşturulacak yolların bir listesini tanımlaması gerekir.
Next.js tarafından belirtilen tüm yolları statik olarak önceden işler
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: false, // can also be true or 'blocking'
}
}
Basit bir önek kod .
// pages/posts/[id].js
// Generates `/posts/1` and `/posts/2`
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: false, // can also be true or 'blocking'
}
}
// `getStaticPaths` requires using `getStaticProps`
export async function getStaticProps(context) {
return {
// Passed to the page component as props
props: { post: {} },
}
}
export default function Post({ post }) {
// Render post...
}
getStaticPaths’i ne zaman kullanmalıyım?
Dinamik yolları kullanan getStaticPathssayfaları statik olarak önceden oluşturuyorsanız ve şunları kullanmalısınız:
- Veriler headless CMS’den geliyor ise
- Veriler bir veritabanından geliyor ise
- Veriler dosya sisteminden geliyor ise
- Veriler herkese açık olarak önbelleğe alınabiliyor ise (kullanıcıya özel değil)
getStaticPaths return değerleri
Örneğin, adında Dinamik Rotalar kullanan bir sayfanız olduğunu varsayalım pages/posts/[id].js.
- paramsnesnenin değeri, sayfa adında kullanılan parametrelerle eşleşmelidir:
- fallback
— 3 Farklı değer alır bu falback fonksiyonu.
False: Statik olarak oluşmayan sayfalar 404 hata mesajı döndürür. Sık sık yeni sayfalar eklenmiyor ise kullanışlı bir durumdur tam dersi durum için pek önerilmez. Eğer daha fazla yol eklenmesi gibi durumlar söz konusu olursa ve fallback değerinin false ise projenizi yeniden build etmeniz gerekecek.
True: uygulamanızın verilere bağlı çok sayıda statik sayfası varsa (çok büyük bir e-ticaret sitesi gibi) kullanışlıdır. Tüm ürün sayfalarını önceden oluşturmak istiyorsanız, derlemeler çok uzun zaman alacaktır.
Ek bilgiler :
getStaticPropseğer fallback: true ise kullanırken arka planda çalışır
getStaticPaths'i getServerSideProps ile kullanamazsınız.
return {
paths: [
{ params: { id: '1' }},
{
params: { id: '2' },
// with i18n configured the locale for the path can be returned as well
locale: "en",
},
],
fallback: ...
}
Regeneration (revalidate)
Basitçe [id].js olarak tanımlı bir site düşünelim bu sitede ekranda değişen veriler var ise bu verilerin ekrana yansıtmamız gerekiyor . Mesela bir döviz tablosu real time olarak değişem değerlere sahip bu gibi durumlarda server tarafından render alınan sayfaların yenilenmesi için gerekli olan durum.
Next.js, sitenizi oluşturduktan sonra statik sayfalar oluşturmanıza veya güncellemenize olanak tanır . Artımlı Statik Yenileme (ISR), sitenin tamamını yeniden oluşturmaya gerek kalmadan sayfa başına statik oluşturmayı kullanmanıza olanak tanır . ISR ile, milyonlarca sayfaya ölçeklerken statikin avantajlarını koruyabilirsiniz
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// the path has not been generated.
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return { paths, fallback: 'blocking' }
}
export default Blog
Oluşturma sırasında önceden oluşturulmuş bir sayfaya istek yapıldığında, başlangıçta önbelleğe alınmış sayfayı gösterecektir.
- İlk istekten sonra ve 10 saniyeden önce sayfaya yapılan tüm istekler de önbelleğe alınır ve anında gerçekleşir.
- 10 saniyelik pencereden sonra, bir sonraki istek önbelleğe alınmış (eski) sayfayı göstermeye devam edecektir.
- Next.js, arka planda sayfanın yenilenmesini tetikler.
- Sayfa başarıyla oluşturulduktan sonra, Next.js önbelleği geçersiz kılar ve güncellenen sayfayı gösterir. Arka plan yenileme başarısız olursa, eski sayfa değişmeden kalır.
Next.js, belirli bir sayfa için Next.js önbelleğini manuel olarak temizlemek için İsteğe Bağlı Artımlı Statik Yenilemeyi destekler. Bu, aşağıdaki durumlarda sitenizi güncellemeyi kolaylaştırır:
- Başsız CMS’nizden içerik oluşturulur veya güncellenir
- E-ticaret meta veri değişiklikleri (fiyat, açıklama, kategori, incelemeler vb.)
Client-side Rendering
İstemci tarafı veri alma, sayfanız SEO indekslemesi gerektirmediğinde, verilerinizi önceden oluşturmanız gerekmediğinde veya sayfalarınızın içeriğinin sık sık güncellenmesi gerektiğinde kullanışlıdır. Sunucu tarafı işleme API’lerinden farklı olarak, bileşen düzeyinde istemci tarafı veri getirmeyi kullanabilirsiniz.
Sayfa düzeyinde yapılırsa, veriler çalışma zamanında getirilir ve veriler değiştikçe sayfanın içeriği güncellenir. Bileşen düzeyinde kullanıldığında, veriler bileşen montajı sırasında alınır ve bileşenin içeriği, veriler değiştikçe güncellenir.
İstemci tarafında veri getirmeyi kullanmanın, uygulamanızın performansını ve sayfalarınızın yüklenme hızını etkileyebileceğini unutmamak önemlidir. Bunun nedeni, veri alma işleminin bileşen veya sayfalar monte edildiğinde yapılması ve verilerin önbelleğe alınmamasıdır.
function Profile() {
const [data, setData] = useState(null)
const [isLoading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
fetch('/api/profile-data')
.then((res) => res.json())
.then((data) => {
setData(data)
setLoading(false)
})
}, [])
if (isLoading) return <p>Loading...</p>
if (!data) return <p>No profile data</p>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}

Nextjs ile Server-side-rendering(SSR) ve client-site-rendering(CSR)’i keşfedelim.(Uygulama ile beraber)
Herkese merhaba React, VueJS ve Angular gibi Javascript frameworkleri popüler olduktan sonra client-side-rendering, server-side-rendering, prerender gibi kavramlarla sikca karsilasmaya basladik . Her ne kadar dökümanlar ile beraber bu işlemleri öğrenmeye çalışakta oldukça kafa karışıklığına yok açacak bir çok kavram ve sorun karşımıza çıkıyor.
Reac, VueJS ve Angular gibi js frameworklerin çalışma şekillerini söyle özetleyebiliriz.
Browser sunucuya bir istek yapar, sunucu yanit olarak bir .js dosyasi dondurur, tarayici da bu .js dosyasini yorumlayarak HTML+CSS’e donusturur ve sayfa kullanıcının ekranında gözükür.
Şimdi Client-Side Rendering kavramını bir örnek ile hep beraber incelemek için basit bir Nextjs uygulaması geliştirelim.
Tüm basit işlemleri bildiğinizi düşünerek basit bir anlatım yapacağım eğer bahsettiğim aşamaların kodlanmış hazır haline ulaşarak isterseniz repo ile çalışmaya başlayabilirsiniz.
https://github.com/kubilaybzk/nextjs_pokemon_app
Öncelikle eğer bu i
yarn create next-app nextjs_pokemon_app
komutu ile bir nextjs uygulaması geliştirelim.
Daha sonra index.js dosyası içini tamamen temizleyelim.
Bir adet useState ve useEffect hook oluşturalım.
const [pokemon,setPokemon]=useState([]);
Daha sonra UseEffect ile verileri çekelim.
useEffect(()=>{
async function getPokemon(){
const responce = await fetch ("https://jherr-pokemon.s3.us-west-1.amazonaws.com/index.json")
setPokemon(await responce.json());
console.log(pokemon)
}
getPokemon();
},[])
Yukarıda gördüğünüz gibi api içinde bulunan bütün pokemonları çektik ve ekrana yansıttık Front end tasafı ile uğraşmamak için aşağıda bulunan js dosyasını kopyalayıp kullanabilirsiniz.
https://medium.com/media/7ada5049e87d864771891f157e55cab5/hrefStyles klasörü içinde bulunan Home.module.scss dosyasına aşağıda bulunan kodları yapıştırarak hiç css ile uğraşmayalım.
https://medium.com/media/1dd64a584ffeddd50ea6f6f028e05068/hrefProjemizi run alalım ve Sonucu hep beraber görelim.
Gördüğünüz gibi projemiz başarı ile çalıştı ve bütün pokemonlar ekranda gözüküyor :)
Şimdi her bir pokemon için bir detay sayfası yapalım .
Page klasöründe yeni bir klasör oluşturalım adını pokemon yapalım.
daha sonra altına [id].js ismi ile bir js dosyası oluşturalım .
Css uygulamak için styles kalsörünün altına Details.module.css dosyası oluşturalım
https://medium.com/media/c9c1ca045860075606eaa5aacc2dabff/hrefGördüğünüz gibi artık her bir pokemon için basit bir detay sayfasınada sahip olduk.
Şimdi hep beraber Client-Side Rendering hakkında konuşalım.
Nedir bu Client-Side-Rendering: Çok ama çok basit bir anlatım ile web uygulamalarının tarayıcı üzerinden render edilmesi diyebiliriz. Yani kullanıcı bir web uygulaması için request yürürlüğe soktuğu zaman server kullanıcıya Html dosyası yada JavaScript dosyası yollar tarayıcı tarafından indirilen bu dosyalar hemen işleme alınır (yorumlanır) bu işlem sırasında kullanıcı ekranında yükleniyor veya sadece beyaz bir sayfa görebilir. (Bu geliştirilen uygulamaya göre değişir)
Bunun ıspatını söyle yapabiliriz . Öncelikle Bulbasaur ‘ın detay sayfasına girelim.
Şimdi öncelikle Chrome için sayfada herhangi bir boş alanda sağ tık yapalım ve sayfa kaynağını görüntüle diyelim yada klavyemizden CTRL+U kısayoluna basalım.
Kaynak kısmını incelemeye başlayalım.
Sayfada gördüğünüz gibi kaynak kısmında Bulbasaur hakkında hiçbir bilgi gözükmüyor bu oldukça normal çünkü biz client tarafında render aldık yani sayfa kullanıcının tarayıcısında oluştu.
Şimdi söyle bir şeyde deneyelim. İnternet hızımızı yavaşlatalım ve nasıl bir sonuç alacağız onu görelim.
Sayfamızda f12 kısayolu ile geliştirici konsolu açalım ve Network kısmından
slow 3G kısmını seçelim .
Şimdi Bulbasaur için açtığımız detay sayfasını yeniden yükleyelim (F5 atalım :) )
Gördüğünüz gibi sayfamız oldukça yavaş yükleniyor bu yüklenme sırasında kullanıcı sadece beyaz bir ekran görüyor bu yukarıda bahsettiğimiz beyaz ekran görme yada yükleniyor olarak ekranda bir sonucun gözükmesi durumundan sadece bir tanesi, biz uygulamamızda yükleniyor … gibi bir ekran oluşurmadık bundan dolayı kullanıcılar sadece beyaz ekran görecek.
Şu konuya da değinmek istiyorum request’in client tarafından alındığının bir başka ıspatınıda söyle yapılabilir tekrar geliştirici seçenekleri kısmında Network kısmına geçelim burada yaptığımız bütün ayarları geri alalım.
Ve http://localhost:3000/pokemon/1 sayfamızı refresh edelim. Burada gördüğünüz gibi API isteği sonucu 1.json isminde bir dosya gelmiş gözüküyor, bu ne anlama geliyor diye sorarsanız bu bize dataları client tarafında çağırdığımızı gösteriyor.
Ek olarak söyle bir bilgi vermek istiyorum projemizi build ettiğimiz zaman NextJS’nin oluşturduğu sayfaları söyle görebiliriz.
Oluşturulan sayfa sayısının ne kadar az olduğuna dikkat edin lütfen bu konuya ileride değineceğiz.
Client-Side Rendering repo :
https://github.com/kubilaybzk/next_render_types/tree/641539dd676ef774a14c6fb27a4d495915f05cf2
Server-Side Rendering
Şimdi Server-Side Rendering ile uygulamamızı kodlayalım.
Öncelikle index.js dosyamızı SSR ile geliştirelim ,yapmamız gereken ilk işlem state ve useEffectleri kaldırmak. Daha sonra SSR yapmak için getServerSideProps adında bir fonksiyon kullanacağız.
Kesinlikle ama kesinlikle getServerSideProps kullanırken export edilmesi ve asyn olan bir fonksiyon olduğunu unutmamız gerekiyor. Ek olarak bu fonksiyonun bir props return ettiğini de unutmamamız gerekiyor .
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() { // Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
return { props: { data } } // Pass data to the page via props
}
export default Page
Yukarıda basit bir kullanımını görüyoruz. Şimdi kodumuza uygun olarak geliştirdiğimiz uygulamaya bu fonksiyonu entegre edelim ve sonucu inceleyelim.
https://medium.com/media/48ba9c47172b952bdd857af0f36ac1d1/hrefUygulamamızı kapatıp tekrar açalım (Konsol üzerinden CTRL+C yapıp kapatalım ve tekrar npm run dev yazarak uygulamamızı yeniden run edelim .)
Gördüğünüz gibi hiçbir değişiklik yok fakat sayfamızın kaynağını incelemek isteyelim. Yukarıda SSR olmadan geliştirdiğimiz index.js kaynak kodu ile SSR olarak geliştirdiğimiz index.js arasında bulunan farkı görüyorsunuz.
Şimdi ise [id].js ile devam edelim.
https://medium.com/media/87a554217e086941056a592af095ee02/hrefBurada fonksiyonumuza dikkat edersek {{params}} adından bir değişken almış bunun sebebi basitçe bizim sayfamızın statik olmaması [id].js olarak tanımlanmış olması .
Şimdi fark edeceğiniz üzere hiçbir değişiklik yok uygulamamızda fakat . Ivysaur ‘a tıklayıp detaylara bakalım .
Şimdi tekrar bu ekranda sağ tıklayalım ve kaynak kodlarını inceleyelim .
Görüdüğünüz gibi kaynak içine bütün datalar gelmiş . Yani uygulama client tarafınada değil server tarafında render olmuş ve biz sayfada herhangi bir pokemon için detay sayfasına gittiğimiz zaman burada gerekli olan tüm html dataları server tarafında oluşturulup bize yollandı.
Network kısmına bakalım .
Gördüğünüz gibi herhangi bir 1.json 2.json gibi bir dosya yok burada bu işlemin serverside tarafında olduğunun başka bir kanıtı.
Server Side Rendering işleminin genel olarak çalışma mantığı aşağıda bulunan diagram ile basitçe açıklayabiliriz. SSR ile beraber çok daha iyi seo desteği sunarız çünkü Google’ın botları web sitelerinde gezerken kaynak kodlarını incelerler eğer kaynak içinde ürünlerimiz hakkında bilgiler mevcut ise bu daha iyi bir seo deneyimi sağlar . Seo için her ne kadar iyi olsada server tarafından oldukça maliyetlidir (buradaki maliyer fiyat olarak değil .)
Şimdi getServerSideProps fonksiyonundan biraz daha bahsetmek istiyorum .
Burada bilmemiz gereken bir kaç önemli kısım var yukarıda da bahsettiğim gibi bu fonkisyon bir props return ediyor bu return eden props bizim sayfamızın ana main compenentine gelen bir props olarak tanımlamamız gerekiyor. Örnek uygulamamızda ;
export default function Details({ pokemon }) olarak yolladığımız gibi.
İkinci önemli nokta ise bu fonksiyonun bir async fonksiyon olduğu.
- Yalnızca sunucu tarafında çalışır ve hiçbir zaman tarayıcıda çalışmaz.
- Sadece page klasörü altında bulunan js jsx yada tsx dosyaları içine yazılır.
Tekrar Vercel üzerinden deploy edilmiş halini inceleyelim. Şimdi burada gördüğünüz gibi oluşan sayfa sayısı Client-Side-Rendering ile aynı.
Static-Site Generation
Tekrar Vercel üzerinden deploy edilmiş halini inceleyelim. Şimdi burada gördüğünüz gibi oluşan sayfa sayısı Server-Side-Rendering’den kat ve kat fazla api içinde bulunan 796 sayfa bunlar
pokemon/1
pokemon/2
pokemon/3
…
pokemon/796'ya kadar giden sayfalar otomatik olarak oluşturuldu.
Kısaca özetlemek gerekirse. Versel üzerinden build edilen sayfaların tek tek çıktıları bu şekilde.
Client-Side Rendering için ;
Kaynak dosyamız içinde hiçbir bilgi bulunmuyor.
Server-Side-Rendering için sayfamız :
Kaynak dosyamız içinde bilgiler mevcut.(CEO için çok uygun)
Static-Site Generation için ;
Kaynak dosyamız içinde bilgiler mevcut.(CEO için çok uygun)
Elimden geldiğince basit bir şekilde SSR ve CSR’ı anlatmaya çalıtştım umarım yardımcı olabilmişimdir .
Repo :
https://github.com/kubilaybzk/nextjs_pokemon_app
Commit edilen mesajlara göre adım adım kodları inceleyebilirsiniz.

React Memo nedir? React.memo() nasıl kullanılır ?
Çoğu zaman bir React geliştiricisi olarak uygulamanın optimizasyonuna ve performans iyileştirmelerine odaklanmamız gerekir. Üçüncü taraf araçlardan yerel özelliklere kadar çok hızlı bir React uygulama performansını arşivlemenin birden çok yolu vardır.
Konuya başlamadan önce Memoization nedir onun hakkında küçük bir bilgi paylaşmak istiyorum.
Memoization bir yapı değil, bir optimizasyon tekniğidir. En basit anlatımıyla memory kullanımı çok fazla olan yani maliyeti çok fazla olan dataların cachlenerek hafızada tutulması tekniğidir.Bu sayede pahalı işlemi tekrar yapmadan cacheten çağırarak bize daha iyi performans sağlayan bir yöntemdir uygulamamızın performansını arttıran bir yöntemdir.
React Memo nedir ?
React Memo bir Higher Order Component (HOC)’dir. Basitçe bileşenin etrafını saran işlenen çıktıyı hafızada tutan ve gereksiz işlemeleri atlayan bir yapı diyebiliriz. Bunu yaparken component içinde bulunan yada dışarıdan gelen data ile ilgilenir. Ayrıca React Memo, bileşenin, değerin değişip değişmediğini görmek için eski prop ile yeni propsun karşılaştırmasına izin verir. Eğer proplarda hiçbir değişiklik yoksa ,React onu oluşturmayı atlar ve en son oluşturulan sonucu yeniden kullanır. React Memo sadece proplar üzerinden değil oluşturduğunuz component eğer hooklar kullanıyorsa ve buralarda bir değişim söz konusu ise örnek olarak useStatedurumu useReducerveya useContextiçeriği değiştiğinde yine de yeniden oluşturulacaktır.
React Memo nerede kullanılır?
Bu yöntemin sadece bir performans optimizasyonu olarak var olduğunu önceden bilmeliyiz .Bunları nerede kullanacağınız konusunda dikkat etmeniz gereken bazı önemli noktalara şunları örnek olarak verebiliriz:
Eğer pure functional component kullanıyorsanız
Bir bileşenin oldukça sık render olacağını önceden bildiğinizde kullanın
Şİmdi bu sık render olma özelliğine basit bir örnek ile yaklaşalım.
Basitçe Youtube uygulamasını hayal edelim ve örnek olarak elmizde söyle bir comp .olsun.
function VideoDescription({ title, user, viewCount }) {
return (
<div>
<Description title={title} user={user}/>TotalViews: {viewCount}
</div>
);
}
Şuna dikkat etmeniz istiyorum.
Burada biz functional component kullandık class component değil !
Componentimiz props olarak 3 değer alır bunlar title,user ve viewCount.
Şimdi bunu uygulamamızda ekrana bastırmak istediğimiz zaman ne olur ?
<VideoDescription viewCount={0} title="My Song" user="Amy Lee" />
Şimdi burada normal şartlarda viewCount değerimizi bir bir arttırısak ne olur ? VideoDescription componentimiz otomatik olarak yeniden render olur değil mi ? Bunun sebebi içeride olan değişiklik yani bizim viewCount değerimizi arttırmamız. Bu verdiğimiz örnek çok basit bu şekilde milyonlarca Description işlemini ekrana bastığımızı düşündüğünüz ve real-time olarak sonuçların durmadan değiştiğini düşündüğümüz zaman durmadan render olan bir VideoDescription componenti elde etmiş olacağız. Bu uygulamamızın performansını oldukça kötü etkiler Bu nedenle, burada, alt Descriptionbileşenine memoization uygulamak için mükemmel bir durum elde ediyoruz.
Şimdi daha anlaşılır olması için beraber bir uygulama geliştirelim ve onun üzerinde kontrol edelim bu durumu. Şimdi bir Todo uygulaması yapalım.
Öncelikle bir React Projesi oluşturalım ve App.js içine girerek uygulamamızı geliştirmeye başlayalım.
2 adet state ekleyelim .
Birisi listeyi tutmak için todo statetimiz.
İkincisi ise Inputtan gelen textleri tutmamız için bir text state.
Daha sonra değerleri almamız ve listeye eklemizde yardımcı olması için bir adet input ve bir adet button ekleyelim uygulamamıza daha sonra Tüm bu elemanları listeyen bir Todo isminde componentimiz olsun.
https://medium.com/media/dc67f3de2402f5954953fe7ae46f5a3c/hrefTodo componentimiz ise tüm bu listeyi sarmalayan bir yapı.
https://medium.com/media/1f75d65bcbb0349ceebf6c71b4cac0f3/hrefTodoItem iste tek bir liste elemanı.
https://medium.com/media/196d1fadf0220c3a015e8a4780611c8c/hrefŞimdi projemizi run alalım ve ekrana çözüken sonuca hep beraber bakalım.
Gördüğünüz gibi sadece iki adet defauld olarak eklediğimiz liste elemanı gözüküyor Şimdi hep beraber kritik noktalara bakalım..
Geliştirici konsolumuzu açalım.
Gördüğünüz gibi öncelikle bir kere App çalıştı daha sonra Todo bir kere çalıştı ve liste içinde iki item olduğu için iki kere TodoItem çalıştı.
Şimdi bir adet todo listesine eleman ekleyelim ve sonucu görelim.
Ben sonuçlar arasında ayrımlar yapabilmemiz için App içine bir adet
console.log("*******************************") yazdım
Input alanına tıkladıktan sonra klavyeden sadece 1 kere M harfine basalım ve olanları beraber görelim.
Gördüğünüz gibi tekrar render oldu alan.
Şimdi Tekrar ınput alanında E harfine basalım.
Fark ettiğiniz üzere klavyede ınput alanına her ekleme yaptığımız zaman tekrar tekrar render component bunun sebebi text state elemanımızın değişmesi.
Şimdi App compenetimizde olan bir değişim yüzünden neden alt kısımlar etkilensin sadece kendisi etkilensin istiyoruz biz neden child componentler yeniden render olup uygulamızı yavaşlatsın bunun önüne geçmek için öncelikle child componentimize use memo yapısını uygulayalım.
Uygulamanızda React Memo’yu kullanın
Artık React Memo hakkında her şeyi bildiğinize göre, onu gerçekten kullanan bir React componenti nasıl oluşturabiliriz ona bakalım.
const MyComponent = React.memo(function MyComponent(props) {
/* render using props */
});
Yukarıda nasıl React.Memo kullanılır basit bir örneğini gösteriliyor. Şimdi bunu kendi uygulamızda Todo için uygulayalım.
https://medium.com/media/d4fccc410724785df76a5634bb31fea7/hrefTodo compenetimizi şekildeki gibi güncelleyelim.
Tarayıcımızı yada consolumuzu yenileyelim ve yeni bom bom bir konsol ile sonuca tekrar bakalım.
Şimdi tekrar ınput alanımıza tıklayalım ve M harfine basalım.
Gördüğünüz gibi sadece App yeniden oluşturuldu içerik değişmedi.
E harfine basalım ve tekrar kontrol edelim. Sadece App re-render oldu.
Şimdi sadece bu durumda App içinde olan değişikliklerden sadece App in render olmasını sağladık peki Todo içinde bir değişiklik olursa ne olacak ?
Bunun için Todo’ya yukarıda yazdığımı “eM” kelimesini ekleyelim.
Şimdi bir uygulama geliştirdiğimizi düşünelim içinde milyonlarca data var ve bu datayı ekranda listeleyip ekranda o data için değişimler ekleme çıkarma gibi işlemler yaptığımızı düşünelim. Hatta düşünmeden örnek ile beraber çalışalım.
Yukarıda fark edeceğiniz üzere App render oluyor daha sonra Todo içinde bir değişiklik olursa Todo çalışıyor buraya kadar herşey normal değil mi ? Şunu gözden kaçırmayalım Todo yeniden re-render olunca Todo içinde bulunan her bir TodoItem yeniden oluşturuluyor. Şimdi 1.000.000 tane elemanımız olduğunu düşünelim 1.000.001. elemanı listeye eklediğimiz zaman üstte bulunan1.000.000 eleman tekrar render olacak performansı nasıl etkiler hayal edebildiniz mi ?
Bunu engellemek içinde en basit işlem React.Memo() kullanmak. Şimdi biz yeni eklenen her bir elemandan öncesini memory içinde tutmak Bunun için TodoItem comp. güncelleyelim.
https://medium.com/media/94cf3256849179928035857664c3c511/hrefŞimdi Konsolumuzu temizleyelim ve listemize yeni birkaç item ekleyelim.
Gördüğünüz gibi sadece o eklenen eleman render oldu geri kalanlar render olmadı .
İşte React.Memo() bu kadar kolay :)
Umarım yararlı olmuştur teşekkür ederim.

React — Context API Nedir ve Nasıl Kullanılır ?
Şimdi direkt olarak konuya ulaşmadan önce sadece state kullanarak bir uygulamayı nasıl kurgularız ondan sözlü olarak bahsedelim.
Öncelikle bir E-ticaret projesi geliştirdiğimizi düşünelim. Prpjemizin genel yapısı aşağıda gösterdiğim mind map ile daha anlaşılır olacaktır.
Yukarıda görselde gördüğünüz gibi bir adet ana sayfa hesabım ve sepetim sayfaları olan bir uygulamamız olduğunu düşünelim şimdi burada söyle bir senaryomuz var kullanıcı sisteme giriş yapmış mı yapmamış mı ona göre gösterilen içerikler var. Yani biraz daha detaylı bir gösterim yapmamız gerekir ise;
Gördüğünüz gibi örnek olarak Navbar için Giriş yapışmış mı yapılmamış mı bu bilgilere göre kullanıcının adını gösterdiğimiz yada Giriş yap butonunun olduğu varyasyonlar var bunlar kullanıcıdan gelen bilgilere göre şekillendireceğiz.
Kısacası kullanıcının sisteme giriş yapma durumuna göre bütün comp kendi kendilerine farklı farklı varyasyonlara giriyor. Şimdi HomePage.js olarak ana sayfamızı geliştirdiğimizi düşünelim.
Giriş yapıldığı zaman kullanıcının bilgilerinin session içinde tutulduğunu ve bu bilgilere ulaşmak için her www.sitem.com/a www.sitem.com/bawww.sitem.com/a www.sitem.com www.sitem.com/ac gibi sayfalara geçiş yapıldığı zaman bunun tekrar tekrar o sayfalarda session okunarak bilgilerin alındığını düşünelim.
Her bir sayfada bun tekrar etmek ne kadar fala kod tekrarına girer ve yorucu olur değil mi. Söyle basit ve akılda kalıcı olacağını düşündüğüm bir örnek ile başlamak istiyorum örnek ne kadar çok basit ve yapılmaması gereken bir örnek olsa da konuyu kavramak için oldukça iyi bir örnek olduğun düşünüyorum. Bu işlemi yapmak için başka başka bir çok yöntem bulabiliriz ama ben bu yöntemi tercih ettim konuyu anlatmak için. Kodu tamamen pseudocode mantığı ile normal kod yapısı arasında yazarak ilerleyelim .
Şimdi elimizde şöyle bir klasör yapısı olsun. Eğer anlatım ile beraber ilerlemek isterseniz github üzerinden linki clone yaparak benimle beraber ilerleyebilirsiniz.
Öncelikle çok basit bir yapı ile anlatım yapmaya çalışacağım mantığı kavramak için gösterdiğim state yöntemleri, fonksiyonlar ve statelerin oluşturulduğu yerler pek mantıklı gelemeyebilir ama konuyu en verimli ve en mantıklı olacak şekilde anlatmak istediğim için bu yöntemi seçtim.
Github: https://github.com/kubilaybzk/contextapi
Yukarıda gördüğünüz gibi bir NextJs ile sıfır bir proje ve görüldüğü gibi bir comp page mantığı oluşturdum.
Navbar için söyle bir yapımız var.
https://medium.com/media/9fdddd80e54483a550b741f9b323897e/hrefProjeyi ilk defa run alınca söyle bir ekran karşılayacak bizi.
Hadi beraber sağ butonları geliştirmekle başlayalım.
https://medium.com/media/8591a71f266b1581320eb36a1bf7ce98/hrefAlt tarafta bulunan giriş yap çıkış yap butonu ile üst tarafta bulunan butonları karıştırmayın . Alt taraf form yapmak ile konuyu uzatmak istemediğim için eklediğim iki basit button.
Şimdi User’ın giriş yaptığı bir senaryomuz olsun. Bunu biz page klasöründe bulunan index.js ile session okuyarak alabiliyoruz fakat bunu index.js içinde bulunan elemanlara iletmemiz için props olarak iletmemiz gerekiyor.
Bunun için index.js dosyamızda bulunan
<Navbar/> kısmını
<Navbar user={user} setUser={setUser} Logout={Logout} LogIn={LogIn} />
şeklinde güncelleyelim.https://medium.com/media/058ef9fb511c3000767407fc4f48e018/href
console.log sayesinde objenin gelip gelmediğini kontrol ettik.
Şimdi burada önemli olan RightButtons kısmına bu bilgileri tekrar iletelim.
https://medium.com/media/0759b65680e39a02921ed3d39f426ad2/hrefŞimdi RightButtons componentinin içine bu bilgileri başarı ile yolladık şimdi eğer user giriş yapmış ise nasıl görüncek giriş yapılmamış ise nasıl görünecek ayarlamalarını yapalım .
https://medium.com/media/e47b540239922bdeb6a277b6fa004de2/hrefAynı şekilde fonksiyonlarıda buradaki butonların onclick eventlerine aktararak gerekli işlemleri yapabiliriz…
Şimdi bu fonksiyonları yada state elemanlarını başka sayfalarda yada başka yerlerde kullanmak istediğimiz zaman tekrar aynı state elemanlarını oluşturmamız aynı yöntemler ile diğer componentler içine aktarmamız gerekiyor.
Projenin genel kapsamına göre her bir component içine bu user nesnesi belkide user dışında bir çok state yada fonksiyonu yollamamız gerekecek, her bir component için props olarak yollama yöntemini uyguladığımızı düşünsenize ne kadar karmaşık ve ne kadar uğraştırıcı bir işlem.
Belkide bazı componentlerimiz şu şekil olacak.
<Example x={x} y={y} c={c} v={v} a={a} d={d} d={d} />
Yukarıda ki gibi bir componenti yönetmek ne kadar zor olur değil mi ?
Şimdi kısa bir özet geçmemiz gerekirse şimdiye kadar yaptığımız bütün işlemleri miras mantığı ile geliştirdiğimizi düşünebiliriz, yani hep bir üstteki component içinden state olarak obje yada fonksiyonları aldık .Görsel olarak şöyle göstermek daha akılda kalıcı olabilir.
Burada yaptığımız bu işleme Prop Drilling denir. Yani component ağacımızın altlarında bulunan bir child component’e props yoluyla üst componentlerden state aktarılmasıdır.
Tüm bu karmaşık kodlardan kurtulmak içinden CONTEXTAPİ kullanıyoruz.
Hazırsanız başlıyoruzzzzz.. :)
Öncelikle projemizin kök dizinine context adı verdiğimiz bir klasör oluşturalım.
Daha sonra hemen altına authenticator.js isimli bir javascript dosyası oluşturalım.
ContextApı kullanabilmemiz için import etmemiz gerekiyor. Aşağıda bulunan kod ile projemize import edelim.
import React, { useContext } from "react";
const UserContext = React.createContext();
Burada bulunan React.createContext() içine defauld olarak değerler yazabiliriz mesela bir tema context yapısı oluşturmak isteyelim sitemize giren ilk kullanıcının açık renk olan temayı görmesini istersen . React.createContext(‘light’) şeklinde tanımlamalar yapabiliriz.
ContextApi yazarken iki farklı bileşen oluşturmamız gerekiyor. Bunlar consumer ve provider olmak üzere iki farklı bileşendir.
Context.Provider
Her Context nesnesi, tüketici bileşenlerin context güncellemelerine abone olmasını sağlayan bir React Provider bileşeni ile birlikte gelir.
Provider bileşeni, bu Provider’ın soyundan gelen tüketici bileşenlerine geçirilecek olan bir value prop’unu kabul eder. Birçok tüketici bir Provider’a bağlanabilir. Provider’lar ağaçtaki daha derin değerleri değiştirmek için iç içe geçirilebilirler.
Bir Provider’ın soyundan gelen tüm tüketiciler, Provider’ın value prop’u her değiştiğinde yeniden oluşturulur. Provider’ın soyundan gelen tüketicilere (.contextType ve useContext de dahil olmak üzere) yayılması, shouldComponentUpdate metoduna tabi değildir, dolayısıyla herhangi bir bileşen güncellemeyi önlediğinde bile tüketici güncellenir.
Context.Consumer
Context değişikliklerine abone olan bir React bileşeni. Bu bileşeni kullanmak, bir fonksiyon bileşen içindeki bir context’e abone olmanıza izin verir.
Alt eleman olarak fonksiyon verilmesine ihtiyaç duyar. Fonksiyon geçerli context değerini alır ve bir React düğümü döndürür. Fonksiyona iletilen value argümanı, yukarıda bu context için ağaçta en yakın Provider’ın value prop’una eşit olacaktır. Yukarıdaki bu context için Provider yoksa, value argümanı createContext() öğesine iletilmiş defaultValue değerine eşit olur.
Yukarıda bulunan bilgiler biraz teknik gelebilir ilk öğrendiğimde bende çok anlamamıştım fakat üzerinde kendi örneğimizle ilerlediğimiz zaman kendi kendinize provider ne işe yarıyor consumer ne işe yarıyor basit bir şekilde anlayacaksınız.
Şimdi ilk olarak UserContextProvider bileşenimizi oluşturmaya başlayalım.
Not: Context yapımızı ilk kez oluştururken UserContext olarak isimlendirdiğimiz için Provider oluştururken bu ismi kullanıyoruz amacımız karmaşıklığı engellemek yoksa istediğiniz ismi verebilirsiniz.
const UserContext = React.createContext();https://medium.com/media/45efe7d8cbf1c2cbe3045cd153cff498/hrefhttps://medium.com/media/d27d62a43a1897e882b4d85e0cbab5b2/href
Yukarıda gördüğünüz çok basit bi context api yapısı burada UserContext Provider içinde state fonksiyon gibi tanımlamalarımızı yapıyoruz. UserContext.Provider içinde value değişkeni ise bu fonksiyon ve state değerlerimizi ve fonksiyonlarımızı export ettiğimiz, basitçe dışarıdan erişerek kullanmamızı sağlayan alan. ;
index.js içinde yazmış olduğumuz state ve fonksiyonlarımızı Provider içine yazalım . Daha sonra Provider içinde bunları dışarıdan erişilebilecek hale getirelim.
https://medium.com/media/33fb92d543ec2a1eaea7e01f56cb5816/hrefDaha sonra bütün componentlerimize props olarak yolladığımız değerleri silelim.
Şimdi burada yapmak istediğimiz işlem hiç miras yöntemine bulamadan istediğimiz component içinde istediğimiz şekilde bu bilgilere ulaşabilmek.
Öncelikle buna heryerden ulaşmak için bunu _app.js içinde bulunan <Component {...pageProps} /> kısmını sarmalamamız lazım çok korkunç geliyor olabilir ama yapılacak işlem çok basit.
import { UserConsumer } from "../context/authenticator";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
return (
<UserConsumer>
<Component {...pageProps} />
</UserConsumer>
);
}
export default MyApp;
Gördüğünüz gibi sadece Provider ile sardık yani bütün uygulamanın en dışına provider yapısını oluşturduk bütün uygulama aslında bunun içinde çalışıyor gibi düşünebiliriz en basit anlatımı ile.
Şimdi Öncelikle index.js içinde oluşturduğumuz LogIn ve Logout fonksiyonlarımızı çağırarak butonlara bu fonksiyonlarımızı tanımlayalım.
Öncelikle index.js içine girelim ve context yapımızı import edelim.
import { UserContext } from "../context/authenticator";
Daha sonra context yapımızı kullanabilmemiz için React tarafından geliştirilen UseContext fonksiyonunu kullanalım ve fonksiyonlarımızı çağıralım (Context içinde kullandığınız isimler önemli birebir aynı olması gerekiyor import ederken ).
const UserContextDatas = useContext(UserContext);https://medium.com/media/46698dc2482326074c547edec1ed1483/href
const LogInFonkiyonu= UserContextDatas.LogIn;
const LogoutFonk= UserContextDatas.Logout;
Yukarıda gördüğünüz gibi Navbar’a hiç props yollamadık şimdi orada fonksiyonlarımızı ve user ismimizi çağırarak context yapısını ekleyelim ve projemize bi bakalım.
https://medium.com/media/60391bdc43830bca640c0041618f3b98/hrefGiriş yap butonuna tıklayalım .
Gördüğünüz gibi projemiz çalışıyor .
Artık istediğiniz yerde her neresi olursa olsun bu yapıyı kullanarak user objesine ve fonksiyonlara erişebilirsiniz.
Elimden geldiği kadar temel bilgiler ile basit bir anlatım yapmaya çalıştım umarım bu yazımı okuyan bir geliştiriciye yardımım dokunmuştur. Teşekkür ederim.