Mobx Üzerine: Nedir, Nasıl Kullanılır? v2

Erdem Uslu
9 min readApr 25, 2020

Giriş

Neredeyse iki sene öncesinde Mobx üzerine bir yazı yazmıştım. Oldukça giriş niteliğinde bir yazı idi. O yazıdan bu yana Mobx kendi içerisinde bir dizi değişikliğe gitti. Hala daha eski yöntemleri desteklese de artık ‘decorator’ özelliğini kullanmadan da Mobx’i kullanmak mümkün. Bu işlemi ‘babel’ eklentileriyle halletmek yerine, fazladan bir fonksiyon kullandırtarak, tanımladığınız Mobx Store’unun çalışır olmasına olanak sağlamışlar.

Mobx üzerine ilk yayımladığım yazı da iş görür halde. Ancak hem Mobx’in yeni özelliklerini kullanarak yeni bir yazı yazmanın hem de eski yazıdaki bazı problemli yerlerin üstesinden gelmenin iyi olacağını düşündüm. Bunu yaparken de, işi sıfırdan ele alıp, proje oluşturulma kısmını da işin içine dahil etmek istiyorum.

Temel olarak bu yazıda üzerinden geçeceğim noktalar şunlar olacak:

  • Parcel ile React ortamının oluşturulması.
  • Eslint kurulumu.
  • Try/catch kullanımı için gerekli babel paketlerinin eklenmesi.
  • Mobx ve React arasındaki bağlantının sağlanması.
  • Örnek mobx kullanımı.
  • Lyricsovh ile birlikte bir servis yaratarak ‘async get request’ oluşturmak.
  • Yukarıdaki işlemi Mobx Store içerisinden yönetmek.

İlk Kurulum

Medium’da hazırlamış olduğum hemen hemen her yazıda, React uygulamasını ayağa kaldırmak için Parcel kullandım. Bunun için oldukça popüler başka çözümler olsa dahi, derslerin kapsamı doğrultusunda Parcel’in oldukça hızlı bir şekilde iş gördüğünü söyleyebilirim.

Esasen Parcel, kendi ve react kütüphaneleri dışında herhangi bir şeye ihtiyaç duymuyor. react, react-dom ve parcel-bundler paketlerini kurmanız, başlangıçta React uygulamasını ayağa kaldırabilmeniz için yeterli.

Bunu yapmadan önce github’dan bir repo oluşturup, github’un gitignore dosyalarından node için olanı seçerek başlayabiliriz. Projeyi VSCode ile açtığımızda içerisinde sadece .gitignore dosyası olan bomboş bir yapı karşımıza çıkacaktır. Öncelikle VSCode’a bağlı olan terminalden ya da dışarıdan terminale erişerek, npm init -y komunutunu çalıştırıyoruz. Bu komut birazdan yükleyeceğimiz paketlerin listesini ve uygulamaya dair çeşitli detayları barındıran bir json dosyası oluşturacak. Bu dosya package.json. İçerisinde uygulamanın ismi gibi bir çok veri barındırıyor. Esasen bunları -y komutunu npm init’in sonuna ekleyerek otomatil olarak oluşturmasını sağladık. Eğer npm init -y değil de npm init kullanırsanız, bu dosyayı oluştururken kullanacağı veriler için size tek tek soracak ve tercihlerinize göre düzenleyecektir.

Bu işlemden sonra asıl yapmamız gerekene geçebiliriz.

npm i react react-dom parcel-bundler

Bu üç paketi kurarak React uygulamasını çalıştırabilmek için gerekli ortamı oluşturmuş olduk. Ancak ortada ne React uygulamasını içeren .js/.jsx dosyaları ne de Parcel’in baz alıp çalıştırabileceği bir html dosyası mevcut. Dolayısıyla projenin kök dizinine index.html dosyası oluşturmamız gerekiyor. Sonrasında index.html dosyasının içerisine iki önemli ek yapacağız. Bunlardan biri React uygulamasının içerisinde çalışacağı element. İkincisi ise Parcel’in okuyacağı script dosyası.

https://gist.github.com/erdemuslu/13213d76c1b77d7a90cfc9c9de9490c2

Yukarıdaki gibi bir html dosyasını kök dizinde yarattıysanız, Parcel için gerekli olan işlemleri tamamlamışsınız demektir. Artık bir React uygulaması, birazdan hazırladığımızda, Parcel tarafında ayaklandırılabilecek.

Bu noktadan sonra projeye eslint kurulumu yapabiliriz. Bu projede Airbnb’in paketini baz alarak eslint kurlumunu tamamlayacağız ancak bazı yazılım ekiplerinin kendi paketlerini hazırlayarak bir nebze daha detaycı bir yaklaşım izleyebileceklerini bilmekte fayda var. Biz burada genel bir standart sağlayabilmek adına çok fazla detaya girmeyeceğiz.

eslint --init

Yukarıdaki komutu çalıştırdığımızda bir dizi soruyla karşılaşacaksınız.

Sırasıyla bu sorulara aşağıdaki cevapları verdiğinizde sonrasında gelen kurumları tamamladıktan sonra eslint kurulumunu bitirmiş olacaksınız.

How would you like to use ESLint? To check syntax, find problems, and enforce code style

What type of modules does your project use? JavaScript modules (import/export)

Which framework does your project use? React

Where does your code run? Browser

How would you like to define a style for your project? Use a populer style guide

Which style guide do you want to follow? Airbnb

What format do you want your config file to be in? JavaScript

Tüm bu işlemlerden sonra kökdizinde .eslintrc.js adında bir dosya oluşturulacaktır. Uzantısı hem .js olan hem de .jsx olan dosyaları sorunsuzca uygulamamıza import edebilmek için bu dosya içerisindeki rules kısmını aşağıdaki gibi düzenlememiz gerekiyor.

...

ecmaVersion: 2018,
sourceType: 'module', },plugins: [ 'react',],rules: { "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], },};

Airbnb linter’inda gelen .jsx olmayan dosyalarda fırlatılan uyarıları engellememizi sağlamış bulunduk böylece.

Klasör yapınız bu şekle bürünmüş olmalı. React yazmaya başlamadan önce tek eksiğimiz kaldı. React uygulaması içerisinde kullanacağımız try/catch bölümlerinin parcel tarafından tarayıcının anlayabileceği şekle büründürülmesi için baz alması gereken babel eklentisini kurmak. Bunun için,

npm i @babel/plugin-transform-runtime -d

Komutunu çalıştırmamız ve paket kurulumu tamamlandıktan sonra, projenin kök dizinine .babelrc adında bir dosya oluşturmamız gerekiyor.

Bu dosyanın içerisine ise kurulumun yaptığımız ‘plugin’i tanımlamalıyız.

// .babelrc{  "plugins": [    "@babel/transform-runtime"  ]}

Uygulamaya Mobx’in Dahil Edilmesi

Öncelikli olarak kökdizine bir src klasörü ve içerisine de main.js ve App.jsx adında iki adet dosya oluşturmamız gerkiyor. Bilindik kısımları yapacağız. main.js Parcel ile erişip uygulamayı ayaklandıracağımız kısmı. Aynı zamanda Mobx’in ilk tanımlamalarını da burada yapacağız. App.jsx ise React uygulamamızın ilk ‘component’i olarak yer edinecektir.

parcel index.html

Yukarıdaki komutu başlattığımızda, uygulamamızın ‘hot reload’ özelliği ile birlikte localhost:1234 adresinde ayaklandığını göreceğiz. Artık elimizde çalışır durumda bir React uygulaması mevcut. Mobx’i projeye dahil etmeden önce basit birkaç parçayı daha eklememiz gerekiyor. İhtiyacımız olan alt-parçalar sırasıyla:

  • Search.jsx: İçerisinde form barındıracak ve form’un gönderilme durumunda Mobx içerisinde tanımladığımız servise erişerek bize aradığımız kelimeye uygun şarkı sözlerini getirecek.
  • View.jsx: Arama işlemi sonucunda çektiğimiz veriyi burada işleyeceğiz.
Search.jsx
View.jsx

Olabildiğince basit bir şekilde örnek iki ‘component’ı yarattık. src içerisinde components klasörü açıp içerisine yerleştirdik. Sonrasında App.jsx içerisinde iki component’ı da ‘import’ edip doğrudan div içerisine yerleştirdik.

App.jsx

Mobx kısmına geçmeden önce hızlıca bu dosyalarda neyi amaçladığımızı anlatabiliriz. Search içerisinde bir adet input, bir adet button ve onları kapsayan form bulunmakta. Form’un onSubmit’ine bir fonksiyon iliştirerek, butona basıldığında(form gönderildiğinde), formun başka bir sayfaya yönlenmesinin önüne geçtik. ‘handleValue’ fonksiyonu ile input’taki her bir değişiklik sonrasındaki değeri sürekli güncel tutmayı sağladık. Böylelikle bu değeri ‘handleSubmit’ içerisinde kolayca kullanılabilir kıldık. Birazdan hazırlayacağımız servise handleSubmit içerisinde erişeceğiz. Tüm bu işlemleri yaparken, tamamen ‘functional’ bir yapı hazırladık.

Buraya kadar olan kısmın Mobx ile doğrudan bir bağlantısı yok. Sadece Mobx’ı dahil edebileceğimiz mantıklı bir yapı kurmuş olduk. Şimdi aşağıdaki komutları kullanarak uygulamaya Mobx’i dahil etmemiz gerekiyor.

npm i mobx mobx-react

Sonrasında src klasörü içerisine Store.js adında bir dosya oluşturuyoruz. Bu dosya Mobx Store’unu içerisinde barındıracak.

Store.js

En basit haliyle bir Mobx Store oluşturduk. Üç adet değişkeni mobx paketinden dosyaya dahil ettik. Sonrasında Store adında bir fonksiyon tanımladık ki, sonrasında bu fonksiyonu new parametresi ile dışarıya obje olarak fırlatacağız.

decorate ile Store objesi içerisinde nelerin ‘observable’ değişken olduğunu nelerin ‘action’ olduğunu belirttik. Ve Store’u main.js’de çağırmak üzere dışarı gönderdik. Mobx’ın eski sürümlerinde, şu anda da yapılabiliyor, ‘decorator’ ile hangi değişkenin hangi tipe denk düştüğünü belirtiyorduk ve bunun için bir dizi ayar yapmamız gerekiyordu(babel eklentileri vs). Artık decorate adlı mobx paketinden dahil ettiğimiz yardımcı yöntemle buna gerek kalmıyor.

main.js

mobx-react paketinden dahil ettiğimiz Provider’i bir ‘wrapper’ olarak kullandık ve ‘Store’ı tüm React uygulamasında, nerede çağırırsak orada kullanmak üzere, Provider üzerinden dağıttık.

Bu aşamadan sonra yapmamız gereken yine mobx-react paketinden çekerek kullanacağımız ‘inject’ ve ‘observer’ ile Store’daki gerekli verileri kullanacağımız yerde tanımlamak. Bu durumu iyice pekiştirebilmek adına, Store içerisindeki name değerini View component’inde render edeceğiz. Search’de ise değiştireceğiz. Ve bu değişikliğin View’a nasıl yansıdığını göreceğiz.

View.jsx

Parçalarına ayırarak özetlemek gerekirse. View component’ının sadece Store adlı bir props aldığını düşünün. Bunu sanki bir üst component’tan paslanıyormuşçasına tanımladık. Sonrasında tüm View component’ını mobx-react’den çektiğimiz observer yöntemine parametre olarak verir gibi sardık. Böylelikle birazdan inject edeceğimiz Store objesinden çekerek kullandığımız name değerinde herhangi bir değişiklik olduğunda, bu değişikliği uygulama içerisine yansıt demek istedik. export default kısmında ise inject yardımıyla Store’u View’a pasladık. inject yönteminin ilk parametresi <Provider’e gönderdiğimiz tüm props’ları içeriyor ve obje döndürüyor. O dönülen obje içerisindeki her değer ise View’a props olarka geçiyor. Provider’a yukarıda <Provider Store={Store} şeklinde paslamıştık Store’u. Eğer burada <Provider Store={Store} UserName=’Cemil’ olarak ikinci bir props paslasaydık, inject’den dönen stores parametresi içerisinde UserName’e de erişebilecektik. Dolayısıyla sonrasında döndüğümüz obje içerisine onu da View’da kullanılmak üzere paslayabilecektik.

Sayfaya döndüğümüzde, Ali isminin sayfada görüntülendiğini görüyoruz. Şimdi Search.jsx içerisinden yine aynı şekilde Store’a erişerek name değişkenini güncellememiz gerekiyor.

Search.jsx

Dikkat ederseniz, observer’i burada kullanmayacağımız göreceksiniz. Bunun nedeni Search component’ının herhangi bir değişkeni render etmediği sadece Store’daki bir değişkeni güncellemek için Store’dan gelen bir fonksiyonu kullanmasıdır. Bunun için formun içine basit bir buton elementi ekleyip, onClick’ine bir fonksiyon tanımlayıp fonksiyon içerisinde Store’dan gelen fonksiyonu çalıştırdık.

Store.js
...
this
.updateName = (name) => {
this.name = name;};
...

Store’da doğrudan ‘Mehmet’ olarak eşitlediğimiz kısmı da bu şekilde değiştirdik. Böylelikle Search component’ından gönderdiğimiz değerin buraya yansımasını sağlamış olduk.

En basit haliyle uygulamamız Mobx ile yapılandırdığımız bir Store çatısı altında çalışıyor. Şimdi işleri biraz daha karmaşık hale getirebiliriz.

Veri İstekleri Yönetme

Esasen bu bölümün doğrudan Mobx ile ilgisi yok. Elimizde bir api var ve bu adrese belirli parametrelerle bir ‘get’ isteği atarsak, kendisi bize bir sonuç dönecektir. Biz bu işlemi Store içerisinde async olarak yapmak istiyoruz. Bunun içinde bir servis oluşturacağız, sonrasında bunu Store içerisinde kullanacağız.

Store.js ve App.jsx’in olduğu src klasörü içerisine Service.js adında bir dosya oluşturuyoruz. Bu dosya içerisinde yapacağımız şey gayet basit olacak. API’a atılacak olan isteği ve gerekli olan yapılandırmayı burada yapıp, fonksiyonu dışarıya aktaracağız. Böylelikle Store.js içerisinde kullanıp, hata ve veri yönetimini Store’da, API’a erişim işlemlerini ise Service’de halletmiş olacağız.

Service.js

Burada sadece ‘get request’ olduğu için bu şekilde bir yapı oluşturduk. this.get ile tanımladığımız fonksiyonda fetch ile API’a eriştik ve veri isteğinde bulunduk. Bu veri isteğini de kendisine gelen artist ve title değerlerini kullanarak gerçekleştirdik. Store.js’e tekrar dönecek olursak,

Store.js

Anlatımın ilk aşamalarında örnek amaçla verdiğimiz name değişkenine hiç dokunmadık. Hem kafa karışıklığına sebep olmaasın, hem de farklı iki yöntem aynı anda Mobx Store’da varolsun diye. Gördüğünüz üzere status, lyricsData ve LyricsService adında üç farklı değişken tanımladık.

this.LyricsService ile biraz önce oluşturduğumuz Service.js içerisindeki objeyi Store’a dahil ettik. Artık içerisindeki get yöntemine rahatlıkla erişip, isteklerimizi yönlendirebiliriz. status ve lyricsData ise bu isteğin hata ve başarılı olma durumlarına göre müdahalede bulunacağımız değişkenler. Decorate içerisinde hazırladığımız bu değişken ve yöntemlere tiplerini verdik. ‘observable’ ve ‘action’ olarak. Store içerisinde ‘this.getLyrics’ adında async bir fonksiyon oluşturdukç. Bunu daha sonrasında Search component’ı içerisinde kullanacağız. Bu fonksiyon da Service’e yönlendirmek üzere bir obje içerisinden alıp kullanacağı iki değişken talep ediyor. ‘artist’ ve ‘title’. Sonrasında try/catch içerisinde Service’den çağırdığımız fonksiyonun yardımıyla istekte bulunduk ve herhangi bir hata olması durumunda catch içerisinde status’u ‘error’ olarak, olumlu durumda ise try içerisinde kullandığımız runInAction fonksiyonu yardımıyla ‘done’ olarak güncelledik. Elimizde herhangi bir hata olmaması durumu için de lyrcisData değişkenine de gelen veriyi eşitledik. Burada eşitleme yaparken split ve join yöntemi kullanarak “\n” karakterini <br /> tag’i ile değiştirdik.

runInAction’un burada çok özel bir faydası yok. Sadece isteklerin en sonunda dönen veriye göre yapacağınız işlemlerin hepsini runInAction içerisinde kullanmak daha derli toplu bir görünüm sağlıyor. State’e yani status ve data değişkenine yaptığımız tüm güncellemeler istek eyleminin sonunda yapılıyor.

Bu aşamadan sonra Search component’ından getLyrics fonksiyonuna erişip çağırdığımızda API isteğinin başlayacağını göreceğiz. Ancak öncesinde Search component’ı içerisinde ufak bir değişiklik yapacağız. Yapıyı ilk oluştururken bir adet ‘text input’u eklemiştik. Ancak API bizedn, hem ‘artist’ hem de ‘title’ istiyor. Dolayısıyla iki adet input yaratacağız ve value olarak tuttuğumuz ve useState ile kullandığımız değeri ‘string’den ‘object’ olarak güncelleyeceğiz ki her iki input’dan gelen value’yu tek bir yöntemle halledebilelelim.

Search.jsx

Öncelikle ikinci bir input ekledik ve input’ların name ve id’lerini value objesine yazacagımız ‘key’ değerine göre güncelledik. Böylelikle doğrudan e.target.id ile obje key’ini seçip e.target.value değerine eşitleyerek güncelleyebilir olduk. Artık value değeri içerisinde hem artist hem de title barındıran bir objeye dönüşmüş oldu. Form’un onSubmit’ine iliştirdiğimiz fonksiyon içerisinde ise Store’dan çektiğimiz getLyrics’i kullanıyor gözüküyoruz. Böylelikle form gönderildiğinde API isteğini başlatabiliyor olacağız.

Yukarıda görüldüğü gibi form’a sanatçı adı ve şarkı adını girip gönderdiğinizde ve tarayıcının ‘network’ kısmına geldiğinizde, API isteğinin düzgün bir şekilde atılıp verinin geldiğini göreceksiniz. Şu an bu veri, Store içerisinde tanımladığımız yapı itibariyle lyricsData değişkenine yazılmış durumda. Tek bir şey kalıyor geriye. View içerisinde gelen veriyi ekrana basmak.

View.jsx

Store’dan aldığımız iki değişkeni kullanarak View içerisinde basit bir yapı kurduk. Status’un durumuna göre yüklenme, hata ve veri gelme durumlarını ekrana bastırdık. Veri içerisinde daha öncesinde <br /> tag’leri eklediğimiz için, gelen ‘string’i html olarak ekrana basabilmek için React’ın ‘dangerouslySetInnerHTML’ yöntemini kullandık.

Tüm bu işlemlerden sonra artık elimizde sağlıklı bir şekilde çalışan Mobx ile yapılandırılmış bir React uygulaması olduğunu görebiliriz. Eğer uygulama üzerinde biraz daha uğraşıp, React ve Mobx pratiğinizi geliştirmek istiyorsanız, aranan sanatçı ve şarkı isimlerinden bir geçmiş oluşturup, tekrardan o geçmiş içerisindeki sonuçlar üzerinden arama yapılmasını sağlayabilirsiniz. Uygulamanın canlı versiyonuna buradan, kodların bulunduğu repo’ya buradan erişebilirsiniz.

Umarım faydalı bir yazı olmuştur.

--

--