Tam Kapsamlı Bir React Native Proje Örneği: eventy (RTK Query + Storybook + Test)

Ezran Bayantemur
Akbank Teknoloji
Published in
18 min readMay 10, 2023

Herkese merhaba!

Uzun zamandır Medium makalesi yazmaya fırsat bulamamıştım ve güzel bir makale ile geri döneyim istedim. Aklımda tam kapsamlı bir React Native proje örneği hazırlamak vardı ve bu konuyu makale haline getirip sizlerle paylaşmanın faydalı olabileceğini düşündüm. Bu makalede sizlerle Storybook’u ve testleri (Jest + Testing Library) olan, Redux Toolkit entegrasyonu olan ve React prensiplerine uygun yaklaşımlar barındıran bir proje yapacağız. Her bir adımı commit linkleriyle beraber paylaşacağım ve projemizi adım adım tamamlayacağız.

Başlamadan evvel paylaşmak isterim ki; proje ufak kapsamda olacak ve yaklaşımlarımızı aslında over-engineering şeklinde gerçekleştireceğiz. Yani bu kapsamdaki bir proje için RTK ya da Storybook gereksiz olabilir. Fakat bu yazıyı yazma amacım sizlere bu araçları “neden kullanmalısınız” ve “nasıl kullanmalısınız” onu anlatabilmek. Projeyi basit tutmak istedim çünkü asıl amacım kullanılan araçları ön plana çıkarabilmek. Yani Storybook, RTK vs. nedir ve neden ihtiyaç duyarız buna odaklanmak istiyorum asıl olarak. Karmaşık bir proje seçmek konuyu daha zorlaştıracak ve uzatmış olacaktı. O yüzden araçlara odaklı gitmek daha sağlıklı olacaktır dostlar. :)

Ön not: Makale yazarken dil kurallarına uymaya özellikle dikkat ediyorum. Buna rağmen bu tarz teknik makalelerde İngilizce terim çok fazla olduğu ve Türkçe karşılığı konunun kapsamından dışarı çıkarttığı için Türkçe-İngilizce karışık bir makale ortaya çıkabiliyor. “component’i”, “endpoint’leri” gibi kelimeler benim de hiç hoşuma gitmiyor fakat teknik yazı için kullanmak zorunda kalıyor insan. Şimdiden mazur görün lütfen. :)

O zaman hazırsak, başlayalım! ☕️

“The Office 💼”

Proje: eventy

Geliştireceğimiz uygulama İzmir Belediyesi’nin Açık Veri Portalı’ndaki kültür sanat etkinliklerinden beslenen ve kullanıcıya etkinlik listesini sunan bir mobil uygulama olacak. Kullanıcı, etkinlik listesini görebilecek, etkinlik detayına gidebilecek ve etkinlik ismine göre filtreleme yapabilecek. Kapsam açısından basit ve sade bir uygulama olacak.

Uygulama için kullanacağımız API endpoint’ini burada bulabilirsiniz.

Not: İzmir belediyesinin Açık Veri Portalı birçok veri kaynağı sunuyor. Belirli başlıklarda API endpointleri mevcut ve bu makaledeki gibi pratik amaçlı projeler için rahatlıkla kullanılabilir. Eğer incelemek isterseniz linki burada mevcut.

Şimdi, ilk adım olarak projemizi oluşturalım

npx react-native init eventy

Projeyi Github üzerinden ilerleteceğim. İlk commit’in linki burada mevcut. İsterseniz repodan da inceleyebilirsiniz. Projeyi oluşturduğum React Native sürümü 0.71.6 bilginiz olsun dostlar.

Peki, tasarımımız nasıl olacak?

Tasarım kabaca yukarıda görüldüğü gibi olacak. Geliştirme yaptıkça detaylanacak elbette fakat taslak olarak bir splash screen (yazıya dahil etmeyeceğim, reponun son halinden inceleyebilirsiniz), bir etkinlik listesi ve etkinlik detayı ekranı olsun şimdilik. “Etkinlik Sayfasına Git” butonuna basıldığında etkinliğin link’i açılsın, “Takvime Ekle” butonuna basıldığında da takvim uygulamasına etkinliği eklesin, “Adrese Git” denildiğinde de harita uygulamasında etkinliğin yapılacağı konum açılsın diyelim. Bahsettiğim gibi, fonksiyonel olarak basit bir uygulama olacak.

Component yapılarımızı inceleyecek olursak da: 🔍

Toplamda dört adet UI component’ımız ön plana çıkıyor. Bu component’ların testlerini, story dosyalarını ve tip tanımlamarını yapacağız. Yazı ilerledikçe başka component’lar da eklenebilir, hep beraber bakacağız onlara da 🤓

Not: Tasarımlar üzerinden component yapısını oluşturma React’ın ana prensiplerinden biri. Bu adımı anlatan güzel bir örnek React’ın kendi dökümanında şu linkte mevcut. Okumanızı tavsiye ederim.

Component’ları yazmaya başlayalım!

Test Driven Development (TDD) Yaklaşımıyla Component Yazma 🧪

“The IT Crowd 💻”

TDD yaklaşımını belki duymuşsunuzdur. Duymayanlar için bir cümle ile özetlersek; “önce testi, sonra kodu yaz” diyebiliriz. Bu ne demek derseniz de yapacağımız bir geliştirme için önce o geliştirmenin sağlaması gereken özellikleri belirleyip bunu test koduna dökme, daha sonra da o geliştirme ile alakalı kodları yazmaya başlama yöntemidir. Bu yöntemin en güzel yanı hata yapma ihtimalinizi azaltmasıdır. Çünkü baştan kurallar belirlenir; kod, o kurallara göre yazılır. Böylece hem eksik bir özellik kalmamış olur hem de yazılan kodda hata çıkma ihtimali oldukça aza indirgenir.

O halde bu yaklaşımla component’larımızı yazmaya başlayalım.

Test Ortamı Kurulumu

React Native’de unit test için kullanacağımız kütüphane topluluk tarafından da çokça tercih edilen testing-library/react-native olacak.

“E bi’ dakika, biz test için jest kullanmayacak mıyız?” diyenler olabilir. Jest’i zaten kullanacağız fakat jest bir test framework’ü. React’tan bağımsız olarak kullanılabilen bir test aracı. testing-library ise aslında react-test-renderer üzerine inşa edilmiş, test ortamımızda component’larımızı render etmemize olanak sağlayan bir araç. Ben yazdığım componentin ekranda istediğim şekilde render olup olmadığını kontrol etmek istiyorum. Test ortamında bu component’ı nasıl render edeceğim? İşte bu soruya cevap veren araç testing-library/react-native oluyor. Alternatifleri elbette var ama bu araç sıkça tercih edilen bir seçenek. Biz de ondan ilerleyelim dedim.

yarn add --dev @testing-library/react-native

Şimdi, gelelim EventCard component’ımıza.

TDD ilerleyebilmemiz için component‘ımızdan neler beklediğimizi iyi belirlememiz lazım ki testleri yazarken neleri ele alacağımızı iyi bilelim.

İlk olarak olay akışından bakacak olursak; kullanıcı EventCard component‘ına tıklayınca detay sayfasına yönlendirilecek. O halde bu card component’ı basılma aksiyonuna cevap veren bir component olmalı. “onSelect” adını vereceğimiz bir prop tanımlayıp bunu card seçilme aksiyonuna bağlayabiliriz. Ek olarak etkinlik verilerimizde “UcretsizMi” adında boolean tipinde bir property gönderiliyor. Ücretsiz olan etkinliklerde bu duruma yönelik bir badge gösterebiliriz component’ımızda. Bir de etkinliğin türüne göre bir tür badge’i ekleyebiliriz.

Bu durumda aslında üç adet test case’imiz ortaya çıkıyor

  • Tıklandığında onSelect event’ini tetiklemesi
  • Biletsiz etkinliklerde ücretsiz badge’ini göstermesi
  • Etkinlik türüne göre tür badge’ini göstermesi

O halde component kodlarımızı yazmadan evvel test kodlarımızı yazmaya başlıyoruz.

EventCard.test.tsx

Kodlarımıza baktığımızda bahsettiğimiz üç koşul için de test case’lerimizin tanımlandığını görüyoruz. İlk case’de component’ımızın touchable elementine gidilip onPress action’ı tetikleniyor (Direkt olarak kullanıcının ekrana basma simülasyonu aslında) ve ardından component’ımızın onSelect prop’unun tetiklenmesi bekleniyor. İkinci case’de de UcretsizMi property’si true olan bir data verilip ilgili badge’in mevcut olması ve aksi bir durumda da ilgili badge’nin ekranda olmaması bekleniyor. Üçüncü case’de de belirlenen türe göre doğru badge’in basılıp basılmadığı kontrol ediliyor. Aslında bu componentin yapması gereken her şeyi tanımlamış oluyoruz.

Terminale gelip yazdığımız testi --watchAll flag’i ile çalıştırıyoruz. Bu flag sayesinde biz test kodumuzu her kaydettiğimizde ilgili testleri tekrar tekrar koşacak.

Biz de her koşulun yeşil tik aldığını görene kadar kodları yazmaya devam edeceğiz. 🤓

Şimdi, TDD ile ilerlesek nasıl bir çıktı olacak, ufak bir GIF ile bakalım.

İlk adımda testlerimiz fail oluyor. Çünkü GenreBadge component’ı henüz oluşturulmamış ve component’ta bulunamıyor. Ekledikten sonra testlerimiz koşunca o testin yeşil tik ile geçtiğini görüyoruz :) Ben takibi kolay olsun diye tek tek ekledim gereken kod satırlarını. Testimizin gerekliliğini sağladıkça sağda görebildiğimiz üzere pass testlerimiz gelmeye başlıyor. En sonunda da componentimizi tamamlamış oluyoruz. 🤘

Bu şekilde önce testi, sonra da kodları yazmış oluyoruz. Basit anlamda TDD bu şekilde bir disiplin.

GIF üzerinden kodların okunması zor olabilir. Component’ın dizini aşağıdaki linkte mevcut, daha rahat inceleyebilirsiniz.

Ben TDD örneğini EventCard component’ı üzerinden verdim, sizler isterseniz diğer component’larda kendiniz TDD mantığını deneyebilirsiniz. Diğer component’ların testleride repoda mevcut.

Peki ya, Storybook? 📕

“Better Call Saul ⚖️”

Component’larımızı oluşturduk. Button, SearchBar vs. gibi UI yapılarımız kullanıma hazır. Peki ya bunları kullanmak isteyen kişilere bu component’ların hangi özellikleri olduğunu göstermek istersek? Evet, “aç kodu oku” belki bir seçenek olabilir. Fakat daha iyi bir yöntem var,

Storybook!

Kullanmamış olanlar için bahsetmek gerekirse Storybook sayesinde yazdığınız component’ların neler olduğunu, hangi propları alabileceğini ve daha da genel anlamda hangi component’ların olduğunu arayüz üzerinden gösterebiliyorsunuz. Benim component’ım hangi propu alır, o propu alırsa nasıl render olur sorularına direkt olarak cevap verebiliyorsunuz.

Storybook aslında web tabanlı framework’ler için geliştirilmiş bir JS tabanlı bir araç. React Native de temelde aslında NodeJS tabanlı bir geliştirme ortamı sunduğu için Storybook kullanmamıza imkan tanıyor. Çalışma mantığı da şöyle; Storybook size aslında app içinde ayrı bir React Native uygulaması sunuyor. Projeyi ayağa kaldırırken onu seçerseniz Storybook kütüphaneniz ekrana yansıyor. Sonra tekrardan değiştirip kendi uygulamanıza geçebiliyorsunuz. Production’a dahil olmuyor tabii ki bu durum ve oldukça da kullanışlı.

Hadi başlayalım! ⚡️

Storybook Kurulumu

Stoybook’un kendi Github sayfasında ana kurulum adımları Expo üzerinden anlatılmış. Fakat Expo olmadan da kullanabiliyoruz. Bunun için ilk olarak Storybook’u projemize kuralım;

npx sb@next init --type react_native

Bu komut projemize Storybook için gerekli olan paketleri ve dizinleri kuracak. Kurulum tamamlandıktan sonra proje dizinimizde .storybook adında bir klasör oluşacak.

.storybook

Storybook kendi arayüzünde kullanacağı react-native-safe-area-context, react-native-async-storage gibi paketleri de yanında getirir. Eğer iOS ortamı için de geliştirme yapıyorsak bu paketler için pod’ları kurmamız gerek.

Bunun için iosdizini altında;

pod install

Son adım olarak da metro.config.js dosyamızda inlineRequires parametresini false yapmamız gerekiyor.

metro.config.js

Meraklısı için: Bu parametre Metro Bundler (Yazdığımız JS kodlarını native tarafa aktaran, projeyi başlatırken açılan terminal) çalışırken React Native’in inline requires özelliğini devre dışı bırakacaktır. Bu özellik lazy loading mantığında çalışır. Siz bir dosyada en tepede import yaptığınızda JS o dosyayı çağırırken en tepedeki import’ları da yanında direkt olarak getirir ve yükler. Inline Requires özelliğini kullanırsanız herhangi bir import’u yanlızca ihtiyacınız olduğu yerde require(‘../component/MyComponent’) gibi kullanmanıza olanak sağlar. Yine o componenti ya da paketi, fonskiyonu vs. artık neyi import ediyorsanız yükleyecektir ama yalnızca ihtiyacı olduğu anda bunu yapacaktır.

Bu değişikliği yapmazsanız Maximum call stack size exceeded hatasını alabilirsiniz.

Bu adımdan sonra kurulum tamamlanmış oluyor. Storybook ekranını görmek için tek yapmamız gereken şey index.js dosyasında Storybook projesini ana proje olarak göstermek. Bunun içinde şu şekilde güncelleyebiliriz dosyasımızı;

index.js

src/App dosyamız bizim ana uygulamamız. Projemize devam edeceğimiz zaman o import’u seçip uygulamamızı çalıştıracağız. UI kütüphanemizde çalışacağımız zaman da Storybook’un bize sunduğu import’tan devam edeceğiz.

Projemizi yarn ios ya da yarn android diyerek çalıştırabiliriz.

Bu adımda hata alırsanız: Metro Bundler’ı manuel olarak yarn start --reset-cache komutuyla çalıştırmanız gerekebilir. Ben safe-area-context paketinden ötürü hata almıştım ilk denediğimde. Bir kaç kez reset cache komutundan sonra düzeldi problem. Projenizi garantiye almak için node_modules dizinini silip yarn install ile tekrar kurabilirsiniz.

Projemizi çalıştırdığımızda ise;

.storybook

Storybook ekranlarımız geldi!

Alt menüde yer alan Sidebar, component listemizi, Canvas, seçtiğimiz component’ımızı, Addons ise o component’a atanan prop’ları kontrol etmemizi sağlayan menü panelleridir. Varsayılan olarak MyButton adında bir component örneğiyle geliyor Storybook.

Şimdi ise kendi component’larımızı yazma vakti ✍️

“I know Storybook”

Component’larımızın Story’leri

Elimizde şu an hangi component’lar var? EventCard, Button, SearchBar. SearchBar için bir story yazalım.

Story’leri proje dizininde istediğimiz yerde saklayabiliriz. Kurulumda oluşturulan .storybook/stories altında tutan da oluyor, component dizininde saklayan da. Ben component dizininde saklamayı tercih ediyorum çünkü o component ile alakalı birimlerin birbirine yakın olması bana şahsen daha uygun geliyor. Eğer siz de bu şekilde saklamak isterseniz Storybook’un component dizinindeki storyleri bulabilmesi için şu ufak ayarı yapabilirsiniz.

.storybook/main.js

Devam edelim.

SearchBar component dizinimizde SearchBar.stories.tsx adında bir dosya oluşturalım ve içini aşağıda gösterildiği gibi dolduralım. Projeyi çalıştırdığımızda sağ tarafta görüldüğü gibi bir çıktı verecektir.

Projeyi başlattığınızda simülatörde alt tarafta mybutton story’sini bulamadığına dair bir hata görebilirsiniz. Önemli bir problem değil, es geçebilirsiniz. Storybook son incelenen story’i async-storage ile belleğine yazar ve proje ayağa kalkınca ilk olarak o story’i açar. Son incelenen story silinirse açılışta bulamaz ve o uyarıyı verir. Component listesinden yeni bir component’a tıkladığınız zaman hata düzelir. Aklınızda bulunsun dostlar! (:

Yukarıdaki kod ekranından gidecek olursak SearchBarMeta adındaki variable bizim component’ımızın ana story bloğu. title parametresi story listesinde hangi isimle gösterileceği, component parametresi ise hangi component’ın story’sini yazacağımızı temsil ediyor. Bu meta değeri default olarak export ediliyor (8. satır) ve Sidebar panelinde yer alan listeye düşüyor.

Meta değerinin içerisine bu component’ın alacağı prop’ları vereceğim ve onSearch parametresinin tetiklendiğini göstermek için de argTypes parametresine ilgili action’ı vereceğim. Bu verdiğim paramterler Addons panelinden incelenebilecek. Çok detayına inip boğmak istemiyorum; args adından da anlaşılacağı üzerine bu componentin alabileceği propları temsil ediyor ve Addons’tan kontrol edebilmemize olanak sağlıyor. argTypes’da tetiklenen metotların hangi tipleri döndüğünü görmemize olanak sağlıyor.

Altta Basic adıyla verilen export ise bu component’ın bir varyasyonu. Sizler istediğiniz isimle istediğiniz kadar varyasyon oluşturabilirsiniz. Ben örnek olması için birazdan bir listenin filtrelendiği bir varyasyon oluşturacağım.

Meta objesinin doldurulmuş haliyle projeyi yenilediğimizde Addons paneli çıktımız şöyle oluyor:

autofix gibi autofix. there’den Theresa’ya

Şimdi bu component özelinde bir varyasyon oluşturalım. Elimizde temel bir data listesi olsun ve bu listeyi component’ımız ile filtreleyelim. Amacımız bu SearchBar component’ı nasıl kullanılabilir karşı tarafa anlatmak.

İlgili varyasyonumuzu hazırladığımızda, çıktımız şöyle oluyor;

GIF’te kodlar okunmuyor olabilir, kodların linkini de paylaşayım.

Dediğimiz gibi, amacımız component’ımızın nasıl kullanılabildiğini anlatmak. Siz yazabildiğiniz kadar story ekleyebilirsiniz. Ne kadar farklı kullanım eklerseniz, karşı tarafa o kadar component’ı tanıma şansı vermiş olursunuz.

Storybook’umuzun amacı da bu zaten :)

Diğer component’ların de örnek story’leri GitHub repo’sunda mevcut. index.js dosyasından App’i Storybook’a çevirip projeyi çalıştırdığınızda inceleyebilirsiniz.

Devam edelim!

State Management? Redux Toolkit! 🧰

Geldik Redux Toolkit konumuza.

Dwight.js

State management, React projelerinin olmazsa olmazı. Projemiz büyüdükçe component’larımız, sayfalarımız artıyor ve ihtiyaçlarımız kompleksleşiyor. Günün sonunda da hem bu karmaşayı kontrol altına alabilmek hem de yeni isterleri rahat entegre edebilmek için state management ihtiyacımız doğuyor. Bu alanda sektörde de en çok sevilen araçlardan biri Redux. Biz de Redux’ın önerdiği Redux Toolkit (RTK) aracını uygulamamıza dahil edip bu entegrasyon nasıl yapılmalı onu inceleyeceğiz.

State management nedir ve hangi soruna çözüm getirir detaylıca bakmak isterseniz daha önceden hazırladığım React State Management başlıklı makalenin ilk bölümüne göz atabilirsiniz. Yazı ilerledikçe de bahsedeceğim bu konudan.

Başlamadan evvel bu noktada React Navigation ile projemize bir Events bir de EventDetail sayfası ekleyelim. Şimdilik sayfaların içi boş kalabilir.

Navigation yapısını oluştururken problem yaşarsanız bu adımın commit link’inden, dosya değişikliklerini kontrol edebilirsiniz;

Redux Toolkit Kurulumu

İlk olarak dependency’lerimizi projemize ekleyelim.

yarn add @reduxjs/toolkit react-redux

Paketlerimiz yüklendikten sonra src altına redux adında bir klasör oluşturalım. Siz isterseniz farklı bir isim de verebilirsiniz.

Şimdi, ilk olarak redux klasörümüzün altında store.ts adında bir dosya oluşturalım. Bu dosya, oluşturacağımız reducer’larımızın toparlanacağı Redux nesnemiz olacak.

Şimdilik şu şekilde store.ts dosyamızı oluşturalım.

src/redux/store.ts

Bu adımdan sonra aynı dizinde ReduxProvider.tsx adında bir dosya oluşturalım. Bu dosyada store nesnemizi react-redux’ın Provider component’ına paslayacağız ve bu component’ımızı App.tsx dosyasında sarmalamak için kullanacağız.

Bu dosyamızı da şu şekilde oluşturabiliriz.

src/redux/ReduxProvider.tsx

Bu noktada “Zaten bir iki satır bir şey yazacağız, ne gerek var ayrı bir dosya kullanmaya?” diyebilirsiniz. Fakat kendine ait config’leri bulunan/bulunabilecek olan yapıları bu şekilde soyutlamak daha kontrollü bir yapı kurma imkanı veriyor bence. Ek olarak App.tsx dosyasını da şişirmemiş oluyoruz bu şekilde.

Bu adımdan sonra oluşturduğumuz ReduxProvider component’ımızla App.tsx dosyamızdaki component’ı sarmallayalım.

src/App.tsx

Bu adımdan sonra Store does not have a valid reducer hatası alabilirsiniz. Bunun sebebi store dosyamızda henüz bir reducer eklenmemiş olması.

Haydi ekleyelim.

RTK Query: eventsAPI

Şimdi, bizim ihtiyacımız olan şey kültür-sanat etkinliklerinin verilerini çekmek ve Events ekranında bunları göstermek. Bunun için eğer Redux Toolkit kullanıyorsak elimizde çok kullanışlı bir araç var.

O da RTK Query.

Bu araç Redux’ın Style Guide’ında da veri çekme için kullanılması tavsiye edilen, RTK’ın bize sunduğu oldukça güzel bir araç. Bir API’ın birden fazla endpoint’den yararlanıyorsak projemizde her bir data için teker teker URL tanımlayıp, veri transformasyonu yapmak yerine bu aracı kullanarak verilerimizi çekebiliriz.

Bunun için öncelikle src/redux dizinimizin altına api adında bir klasör açıp, içinde de events.ts adında bir dosya oluşturalım. Dosyayı şu şekilde dolduralım;

src/redux/api/events.ts

Kodu incelediğimizde toolkit’in query/react dizininden createApi metodunu çağırdığımızı ve içine reducerPath olarak "eventsApi" verdiğimizi görüyoruz. Bu, bizim uygulama içinde bu yapıya hangi isimle ile ulaşabileceğimizi belirtiyor.

baseQuery paramteresi bizim kullanacağımız API’ın ana URL bölümünü istiyor. Yani sizin kullandığınız API’ın https://myapi.com/users https://myapi.com/todos şeklinde birden fazla endpoint adresi varsa bu parametre sadece https://myapi.com adresini isteyecektir sizden.

  • Burada bu parametrenin devamında bulunan fetchBaseQuery fonksiyonu; atılacak HTTP çağrıları için Node.JS’in kendi fetch yapısını kullanmak istediğimizi belirtiyor. Ekstrem bir durum olmadığı sürece siz de bunu kullanabilirsiniz.

endpoints ise tahmin edeceğiniz gibi verdiğimiz örnekte /users ve /todos endpoint’lerine karşılık geliyor. Karşılığındaki callback fonksiyonun parametresi olan builder ise bize her yazdığımız endpoint’e karşılık birer custom hook oluşturuyor. Dikkat ederseniz endpoint adımız getEvents ve en altta bulunan custom query hook’umuz ise useGetEventsQuery.

RTK Query her bir endpoint’imiz için özel bir custom hook oluşturuyor.

Oldukça kullanışlı 🤌

Daha fazla detay için createApi dökümantasyonuna bakabilirsiniz.

Hazır “reducer” kavramına girmişken burada ufak bir parantez açıp kendisinden kısaca bahsetmek istiyorum;

Bütün yazılım uygulamaları modül modül planlanır. Örneğin bir sosyal medya uygulamasında profil işlemleri, gönderi paylaşma aksiyonları ve mesajlaşma işlemleri gibi belirli modüller oluşturulabilir. Bu bağlamda reducer’lara uygulamamızda saptadığımız modüllere yönelik oluşturulan Redux yapılarıdır diyebiliriz.

Biraz daha örnekleyelim.

Örneğin; giriş yapma, kayıt olma, token kontrolü, şifre yenilme ve şifre kurtarma gibi işlemleri yetkilendirme başlığında toparlayabiliriz. Haliyle bütün bu aksiyonları yönetecek yapıya authReducer adını verebiliriz. Saydığımız bütün aksiyonları da bu reducer altında tek tek oluşturabilir, istenildiği yerde çağırabiliriz. Örneğin, şifre yenileme hem profil sayfasında kullanılabilir hem de süresi dolmuş şifreler için giriş yapıldıktan sonraki bir ekranda kullanılabilir.

Örneğin; arkadaş olarak ekleme, arkadaşlıktan çıkarma, yakın arkadaşlara ekleme, yakın arkadaşlardan çıkarma ve kişiyi sessize alma gibi aksiyonları friendsReducer başlığında toplayabiliriz. Çünkü mantık olarak birbirine yakın fonksiyonlar ve aynı endpoint’ten besleniyor olabilirler.

Kısaca reducer kavramı birbirine benzer fonksiyonların toparlandığı bir yapıdır diyebiliriz. Bir uygulamada da birden fazla reducer bulunabilir. Hatta bulunmalıdır da.

Reducer yapısı anlaşıldıysa devam edelim.

Şimdi ise store.ts dosyamıza geri dönelim ve aşağıdaki gibi güncelleyelim.

src/redux/store.ts

Burada reducer objemizin içine oluşturduğumuz eventsApi reducer path’ini veriyoruz ve karşısına event reducer objesini atıyoruz. Oluşturduğumuz API artık Redux yapımıza dahil edilmiş oldu.

Bir altta yer alan middleware’de ise eventsApi middleware’mizi bağlıyoruz. middleware için tam kelime anlamı olan ara modül, yardımcı bir etmen diyebiliriz. Bu satırdaki işlem de aslında bizim oluşturduğumuz event query yapısını ana yapımıza dahil etme işlemi oluyor kısaca.

Bu adımdan sonra geriye sadece custom hook’umuzu çağırıp kullanmak kalıyor.

Redux yapımızın sağlıklı çalışıp çalışmadığını kontrol etmek için kullanabileceğimiz güzel bir araç var.

React Native Debugger

Aslında kendisine web tarafında kullanılan Redux DevTools’un React Native versiyonu diyebiliriz. Redux da debug için Redux DevTools’u tavsiye ediyor.

Bunun için Github adresindeki adımları takip edip kurulumu yapalım. macOS için Homebrew’den kurulumu yapabilir, Windows için bu linkte bulunan .exe dosyasını indirip kurabiliriz.

Not: Debugger’ı kullanabilmek için Hermes’i devredışı bırakmak gerekiyor. Bunun için iOS’da Podfile dosyasında 35. satırda :hermes_enabled => false; Android’de ise android/app/gradle.properties dosyasında hermesEnabled=false değişikliklerini yapmalısınız.
Bu işlemlerden sonra iOS’a değişikliklerin yansıması için tekrardan ios dizininde pod install komutunu çalıştırıp proje dizinine dönerek yarn ios demeniz gerekiyor.
İlgili işlemler için dökümandaki bu başlığa bakabilirsiniz.

Kurulumu tamamladıktan sonra çalıştırmadan evvel açılış sayfamız olan Events sayfasına oluşturduğumuz custom query hook’unu çağıralım. Şimdilik ekrana yansıtmaya gerek yok.

Events.tsx

Şimdi debugger’ımızı açıp projemizi çalıştıralım. Developer Menu’yü açıp (Terminale geçip D’ye basabilirsiniz) Debug with Chrome diyelim. Bu komut normalde Chrome Devtools’u açıyor fakat React Native Debugger açıkken debug deyince otomatik olarak debugger’ımıza bağlanacaktır.

Çıktımız da şu şekilde olacak;

Soldaki panelde görebildiğimiz üzere useGetEventsQuery custom hook’umuz başarılı bir şekilde ateşlenmiş ve verilerimizi alabilmiş. React Native Debugger ile Redux kullandığımız zaman hangi action’larımız nasıl çalışmış takip edebiliriz. Bu query’yi bu panelde görebilmemizi sağlayan şey ise middleware olarak dahil etmiş olmamız.

O zaman,

RTK Query işlemimiz tamam!

Bu noktaya kadar olan Redux işlemleri için ilgili commit’i aşağıda bulabilirsiniz

Projemizi Tamamlayalım 🤘

İhtiyacımız olan her şey tamam. Artık projemizi tamamlayabiliriz.

Ne demiştik? Events ekranında etkinliklerimiz görüntülenecek, EventsDetail ekranında da seçtiğimiz etkinliğin detaylarını göreceğiz.

Debugger’ımızı kullanarak Events.tsx dosyamızda çağırdığımız query hook’u ile ihtiyacımız olan verileri çekebildiğimizi gördük. Şimdi o verileri Flatlist ve custom componentimiz EventCard ile ekrana yansıtalım.

src/pages/Events.tsx

Bu adımda ek olarak Loading ve Error component’larını ekledim. Logic olarak kodda da görüldüğü üzere yüklenme ve hata gelme durumlarında koşullu olarak render oluyorlar. Bu işlemler için commit linki aşağıda mevcut.

Not: Ek olarak components dizini için path alias da ekledim. tsconfig.json ve babel.config.js dosyalarındaki değişikliklere bakabilirsiniz. Değişikliklerin yansıması için Metro Bundler’ı cache’ni temizleyerek restart etmeniz gerekiyor. Bunun için proje dizininde yarn start --reset-cache komutunu çalıştırmanız yeterli.

Bu adımdan sonra da etkinlikleri filtreleme işlemini ekleyip EventDetail sayfasına yönlendirme aksiyonunu da projemize dahil edelim.

Ben projeyi daha fazla şişirmemek için filtreleme işlemini direkt olarak sayfa içerisine ekledim. Normalde filtreleme işlemini direkt olarak Redux katmanında yapıp componenti oradan beslemek aslında daha uygun bir yol olacaktır. Hatta filtreleme işlemi için fuse.js kullanmak daha da iyi bir yaklaşım olur.
Şimdilik bu makale içerisinde bu şekilde ilerleyelim, ileride detaylı bir RTK makalesinde buna benzer bir logic üzerine konuşabiliriz ☕️

Bu geliştirme ile ilgili commit linki aşağıda mevcut, isterseniz siz de deneyebilirsiniz.

Şimdi ise yönlendirdiğimiz EventDetail sayfasını tamamlayalım. Bunun için sayfaya yönlendirirken seçilen etkinliğin Id değerini de sayfaya geçirmemiz gerekli. Bunun için navigate fonksiyonuna parametre olarak paslayalım ve pasladığımız değer üzerinden seçilen etkinliğin detaylarını endpoint’imizden çekelim.

Detayları çekmek için eventApi yapımıza yeni bir query ekleyelim.

redux/api/events.ts

Query’mizi oluşturduktan sonra EventDetail sayfamızda çağıralım ve gelen verileri kullanarak etkinlik detaylarını ekrana yansıtalım.

Şekilde görüldüğü gibi etkinlik detaylarımızı çektik ve ekrana yansıttık. Etkinliğin detay bilgileri yer alıyorsa da bunları birer WebView component içinde gösterdik. API’dan gelen HTML verileri pek tutarlı olmadığı için veriler biraz düzensiz görünebiliyor. Ek olarak da en alta eğer adres bilgisi yer alıyorsa “Adrese Git” butonu yer alıyor. Bu buton kondisyonel olarak görüntüleniyor. Bir de etkinlik sayfasının açıldığı bir “Etkinlik Sayfasına Git” butonu mevcut. Bu buton da etkinlik bilgilerinin yer aldığı İzmir Belediyesi’nin sayfasını tarayıcıda açıyor. Detaylı incelemeyi aşağıda yer alan commit mesajından da görüntüleyebilirsiniz:

Ayrıca bu ekranda yer alan yardımcı fonksiyonlar için bir utils dizini oluşturdum. Hepsinin unit testi de mevcut. Onları da bu linkten inceleyebilirsiniz:

Etkinliği Takvime Ekleyelim 🗓️

Şimdi sıra etkinliği takvime eklemeye geldi.

Etkinlik verisi içerisinde etkinliklerin seans bilgileri ve müsaitlik durumları da yer alıyor. Kullanıcının müsait olan istediği seansı takvime eklemesini sağlayalım. Bunun için öncelikle bu seansları etkinlik detay sayfamızda gösterelim.

Component’ımız doluluk olanı 100 olan verilerde takvime ekleme opsiyonu sunmayacak. Diğer durumlarda ise kullanıcı ilgili seçimi yapıp takvime ekleyebilecek.

Takvime ekleme aksiyonunu da bu component içerisinde yapacağız. Çünkü bu aksiyonu EventDetail içerisinde eklemeye gerek yok. Aksi halde SessionCard component’ını kullanacak yerlere gereksiz yere logic taşımış oluruz. Sonuçta component’ımız bu aksiyonu kendi başına oluşturabilir. Başka bir zaman seans gösterme işlemine ihtiyaç duyarsak takvime ekleme işlemini tekrar tekrar yapmamış oluruz.

İlk olarak takvime ekleme için kullanacağımız paketimizi ekleyelim.

calendar-events

Bunun için öncelikle kullanacağımız paketi yükleyelim. Bu native işlemleri bize sağlayan react-native-calendar-events adında bir paket mevcut.

Paketimizi projemize yükleyelim:

yarn add react-native-calendar-events

iOS ortamı için Pod’ları güncelleyelim:

cd ios && pod install

Takvime erişebilmemiz için izinleri de eklememiz gerekli. Android için AndroidManifest.xml dosyasına gelip aşağıdaki satırı ekleyelim:

  <uses-permission android:name="android.permission.WRITE_CALENDAR" />

Son olarak da iOS için Info.plist dosyasına da aşağıdaki satırı ekleyelim:

 <key>NSCalendarsUsageDescription</key>
<string>For adding events to Calendar app</string>

Paketin kurulum işlemleri tamam. Devam edelim.

Not: Kurulum adımlarında bir problem yaşıyorsanız paketin wiki sayfasından adımları kontrol edebilirsiniz.

SessionCard component’ımızda Takvime Kaydet adında bir buton olacak ve basıldığında ilgili etkinliğin tarih aralığında bir takvim etkinliği oluşturacak. Bu mantığa göre component’ımızı testlerimizi açıkta tutarak yazmaya başlayalım.

İlk olarak testlerimizi oluşturalım. Burada TDD yöntemiyle ilerleyeceğiz

Kullanacağımız paketimizi mock’ladık; çünkü test ortamında takvime gerçekten etkinlik eklenmesine gerek yok. Başarılı ekleme işlemini cover’lamamız bizim için yeterli.

Şimdi de fonksiyonumuzun içini dolduralım ve story ekranında verilerimizi kontrol edelim.

Component kodlarını rahat okuyabilmeniz için direkt olarak kod linkini paylaşayım:

Component görünümü de bu şekilde.

Takvime ekleme için ilk olarak kullanıcıdan izin almak gerekiyor. İzin verilme durumunda da etkinliğimiz ekleniyor. İşlem oldukça basit.

Bir etkinlik için kontrol edersek ise çıktımız şu şekilde oluyor:

İşlemimiz tamam. :)

Projemiz tamamlandı. Bu adımdan sonra bir iki ufak UI güncellemesi ekleyeceğim, maksat proje biraz daha güzel görünsün. :)

Projenin bu noktaya kadarki hali aşağıda mevcut, repo’yu inceleyebilirsiniz.

Sonradan ekleyeceğim geliştirmeleri rahatça inceleyebilmeniz için commit commit göndereceğim. Attığım linkteki commit’ten sonra gelenlere bakarak kontrol edebilirsiniz.

Son

“The Lord of the Rings 💍”

Ve projemiz bitti!

Bir video ile göz atacak olursak:

Böylece makalenin de sonuna gelmiş olduk. Uzun ama yararlı olduğunu ümit ettiğim bir yazı oldu. RTK Query, TDD prensibi ve Storybook, projeler için standartı yükselten yaklaşımlar ve sektörde de kendine yer bulan araçlar. Umarım sizler için de bu araçlar ile ilgili faydalı bir makale olmuştur ve sıkılmadan okumuşsunuzdur. :)

Kendinize çok iyi bakın dostlar, kod ile kalın, esen kalın.

Sonraki yazılarda görüşmek üzere!

🎸

“Software and cathedrals are much the same — first we build them, then we pray.”

— Sam Redwine

--

--