React — Context API Nedir? Nasıl Kullanılır ?
Bu yazının demo projesi GitHub’da bulunmaktadır — Demo projesinin bulunduğu GitHub Reposu → Repo
Merhaba arkadaşlar. Bugün sizlere React’ta bulunan Context API özelliğinin ne gibi bir sorun’a çözüm getirdiğinden bahsetmek istiyorum. Öncelikle sorunumuz ile yazıya başlayalım :)
Sorun 😭
React, Context API ile prop drilling denen sorunun önüne geçmeyi amaçlanmaktadır.
Prop Drilling Nedir ?
Prop drilling, bir state’in, component ağacımızın daha yukarısında bulunan bir parent component’ten, component ağacımızın altlarında bulunan bir child component’e props yoluyla aktarılarak state’in ulaştırılmasına denir. Buradaki sorun, state yukarıdan aşağıya doğru aktarılırken, arada köprü olarak kullandığımız componentlerin sadece bu değeri ulaştırmak için gereksiz yere kullanılıyor olmasıdır. Aşağıda bulunan component ağacımızı bir inceleyelim.
Bir state’imiz olduğunu düşünelim (array, nesne veya string türünde bir değer olabilir) ve bunu en alttaki component’te kullanmamız gerekiyorsa, yapacağımız işlem bu state değer veya değerlerini o component’te tanımlamak olacaktır.
Bir süre sonra bu değerin sadece o component’te değil de sibling dediğimiz yanında bulunan başka bir component’te daha kullanmanız gerektiğinde, bu state’i diğer component’te aktarabilmemiz için, state’i daha üstte bulunan bir component’e taşıyarak props yoluyla componentlere aktarmamız gerekecektir.
Aradan bir süre daha geçti ve bu state’in yine aynı şekilde başka component’lerde de kullanılma ihtiyacı olduğunu varsayalım. Yine aynı şekilde state’i diğer componentlere aktarabilmek için, state’i component ağacında daha yukarıda bulunan bir component’e taşımanız gerekecektir.
Biraz daha zaman geçtikten sonra bu aktardığımız state’i farklı bir dal’da bulunan (örneğin Container dalının en altında bulunan) bir component’e aktarmak istediğimiz zaman, o state’i daha yukarıya taşımamız gerekecektir.
Görselde de görebileceğiniz gibi component ağacımızın yukarısında bulunan bir component’ten, altlarda bulunan bir child componente bir değer aktarmak istediğimiz zaman arada birkaç component’e de sadece bu değeri aktarabilmek için props yoluyla göndermek zorunda kalıyoruz. İşte bu duruma prop drilling denmektedir. Prop drilling ile ilgili küçük bir örnek yapalım (Görselin altında bulunan linkten kodlara erişebilirsiniz)
Buradaki örneği sadece fikir verme açısından düşünecek olursak eğer App içerisinde tanımladığımız bir state’i önce HeaderContainer component’ine gönderiyoruz ve daha sonrasında HeaderTitle component’ine props yoluyla aktarıyoruz.
Bu örneğimizde pek fazla props ile veriyi aktarma işlemi gerçekleştirmiyor olsakta, uygulamanızın component ağacının dikey konumda büyümesi durumunda, bu şekilde props ile component ağacının aşağısında bulunan bir component’e state’i gönderdiğinizi düşünün.
Alternatif Çözüm Context API 🤓
Context API’nın burada getirdiği çözüm ise, aktarılmak istenen değerleri global şekilde tutar ve direkt olarak kullanılması gereken component’e aktarılmasını sağlar. Böylelikle bir state’i, component ağacının altlarında bulunan bir component’e aktarmak istediğiniz zaman, her seferinde props olarak göndermenize gerek kalmaz.
Yukarıdaki örneğimizde bulunan title state’ini Context API kullanarak nasıl HeaderTitle Component’ine aktarabileceğimize bakalım.
1- Öncelikle createContext metodunu kullanarak bir context oluştururuz. Bu bize bir Context nesnesi döndürür. Context nesnesi içerisinde Provider ve Consumer adında 2 component bulunmaktadır. (Bunları birazdan kullanacağız.)
2- Daha sonrasında global olarak tutmak istediğimiz state’lerin bulunacağı component’i oluşturmamız gerekmektedir. Bu componentimizi ContextProvider olarak adlandıralım. Title state’imizi App.js’ten alalım ve ContextProvider component’imize ekleyelim. Ayrıca props olarak gönderdiğimiz yerlerden de title’ı kaldıralım.
- Context.Provider içerisinde value adında bir props bulunmaktadır. Buraya diğer componentler tarafından erişilmesini istediğimiz değerleri yazalım.
- props.children, ContextProvider component’inin tag’ları içerisinde yer alan tüm component’lerin, value kısmına yazılmış olan değerlere erişebileceğini gösterir.
Konu dışı olarak props.children ile ilgili mini bir örnek gerçekleştirelim.
props.children
App.js dosyasını inceleyecek olursak eğer. MakeBiggerText adında bir componentimiz bulunmaktadır ve bize h1 tag’ını döndürmektedir.
h1 tagı içerisine yazmış olduğumuz props.children.toUpperCase() ile ulaşılmak istenen, MakeBiggerText component’inin tagları arasına yazılmış her bir text değerini büyük harflere çevirerek ekranda gösterilmesini sağlamaktır.
Yukarıdaki örneğimizden de görebileceğiniz üzere App componenti içerisinde MakeBiggerText component’inin taglar’ı arasında bulunan hello değeri, MakeBiggerText componentine props.children olarak aktarılır. Aktarılan bu children değeri, toUpperCase() ile büyük harfe çevrilir ve gösterilir.
Bu küçük bilgilendirmeden sonra kaldığımız yerden devam edelim :)
3 - Provider component’imizin diğer componentler tarafında erişilebilir olmasını sağlayabilmek için component ağacımızın en üstüne yerleştirmemiz gerekir. App componenti, uygulamamızın en üstünde bulunan component olduğu için, Provider component’ini de App.js içerisinde çağırarak sağlayabiliriz.
- Gördüğünüz gibi oluşturmuş olduğumuz ContextProvider component tag’larının içerisine HeaderContainer component’ini eklemiş olduk. Böylelikle value kısmına sağlamış olduğumuz değerlere HeaderContainer componenti tarafından erişebiliyor oluruz.
Global olarak tuttuğumuz bir değere erişebilmek için, erişmek istediğimiz componentten, oluşturmuş olduğumuz Context’i çağırmamız gerekmektedir. Provider’ın value kısmına yazmış olduğumuz bir değere erişmek için bu seferde Context’in Consumer component’ini çağırarak state’e erişim sağlayabiliriz.
Context.Consumer içerisine yazmış olduğumuz bu fonksiyonunun parametresi içerisinde, Context.Provider’a props olarak vermiş olduğumuz value değerleri bulunur.
Bu değerleri görebilmek için öncelikle context’i, tarayıcımızın konsoluna yazdıralım.
Gördüğünüz gibi Provider componentimizin value kısmına göndermiş olduğumuz title state’ine, HeaderTitle componentimiz içerisinde, Context’in Consumer component’ini kullanarak erişim sağlamış olduk.
Artık yapılması gereken son şey olarak context parametresi içerisinden bu değeri çıkartarak kullanmak. Elimizde tek bir value değeri olduğu için direkt olarak context’i kullanabiliriz.
Provider Componentine Yazılan Bir Metod İle State Değerini Değiştirme
Şimdi biraz daha ileriye götürelim ve bir butona tıklanması durumunda title’ın değiştirilmesini sağlayalım. Ancak bu değişikliği de Provider içerisine yazacağımız bir metodu kullanarak yapalım.
Öncelikle Provider Componentimiz içerisine handleTitleChange adında bir metod tanımlayalım. Tanımladığımız bu metodun diğer componentler içerisinde de kullanılabilmesini sağlayabilmek için value içerisine aynı zamanda referans olarak bu metodu verelim. value ile birden fazla değeri erişelebilir yapabilmek için göndermek istenilen state’leri, metodları bir nesne içerisinde göndermemiz gerekmektedir.
HeaderButton adında bir component oluşturalım ve oluşturduğumuz bu component içerisinde bir buton tanımlayalım.
Bir önceki örnekten hatırlayabileceğiniz gibi context ile Provider’ın value props’u içerisine yazmış olduğumuz değerlere erişim sağlayabiliyorduk. Title state’ini değiştirecek olan metodu, oluşturduğumuz butonun onClick event’ine referans olarak veririz. Böylelikle butona tıklandığı zaman Provider içerisinde bulunan handleTitleChange metodu tetiklenir ve HeaderTitle içerisinde bulunan title değerinin güncellenmesi sağlanır.
HeaderButton componentimizi de tanımladıktan sonra HeaderContainer Component’i içerisinde çağırıp uygulamamızı çalıştırırız.
Demo kodlarının bulunduğu GitHub Reposu → Repo
Umarım faydalı bir yazı olmuştur. Eksik veya yanlış olduğunu düşündüğünüz kısımları bana iletirseniz çok sevinirim :)
İletişim kanalları: Twitter — LinkedIn — Mail
Bir sonraki yazıda görüşmek üzere!
Bu yazının referansları:
https://www.toptal.com/react/react-context-api
https://www.digitalocean.com/community/tutorials/react-manage-user-login-react-context