* * *

Özet: React uygulamalarında state'ler prop'lar yardımıyla parent (üst) componentlardan child (alt) componentlara aktarılır. Uygulama genelinde state'nin aktarıldığı component seviyesi fazla ise yani state birden fazla iç içe componenta aktarılmak isteniyorsa uygulamamız yönetilemez hale gelebilir. React Context'ler component ağacında istediğimiz veriyi prop'lar üzerinden taşımadan componentlar arasında taşımayı sağlar.

React ile uygulama geliştirirken state'ler tanımlarız ve bu state'leri kullanacağımız componentlara prop'lar yardımıyla aktarırız. Aynı seviyedeki componentlarda bu state'i kullanmak istediğimizde ise state'i bir seviye üstteki component'a taşımak zorunda kalırız. Bu akış basit uygulamalarda problem olmayabilir. Fakat uygulama büyüdükçe ve component sayısı arttıkça state'i yönetmek ve component'ların bu state'i kullanmasını sağlamak büyük bir problem haline gelebiliyor. Bu problemi ufak bir örnekle anlamaya çalışalım.

import React, { useState } from 'react'
import { render } from 'react-dom'

const ThemeSwitcher = ({ theme }) => (
  <div>
    <button>{theme === 'dark' ? <span>🌞</span> : <span>🌙</span>}</button>
  </div>
)

const Header = ({ theme }) => (
  <header>
    <Navbar />
    <ThemeSwitcher theme={theme} />
  </header>
)

const App = () => {
  const [theme, setTheme] = useState('dark')

  return (
    <div className={`${theme === 'dark' ? 'dark-theme' : ''}`}>
      <Header theme={theme} />
    </div>
  )
}

render(<App />, document.getElementById('root'))

Örnekte de görüldüğü gibi theme state'ini iki seviye alttaki ThemeSwitcher componentına prop'lar yardımıyla aktardık. Peki bu state'i 10 seviye alttaki bir component'a aktarmak isteseydik ne yapacaktık 🤔? Bu ve bunun gibi problemleri çözmek için React Context API kullanmamız gerekiyor.

React Context API Nasıl Kullanılır?

React Context API Nedir?

Paylaşılabilir bir state oluşturmak için ilk yapmamız gereken bir context oluşturmaktır. Context oluşturmak için React.createContext() metodunu kullanılırız. Bu metod oluşturduğumuz context için default değer belirleyebileceğimiz bir parametre alır.

const ThemeContext = React.createContext('light')

const AuthContext = React.createContext({
  isLoggedIn: false,
  user: null
})

const MoviesContext = React.createContext({
  movies: []
})

Yukarıdaki örnekte olduğu gibi farklı tiplerde context'ler oluşturabiliyoruz. Oluşturduğumuz context'i kullanabilmek için Context Provider ve Context Consumer olmak üzere iki adet özel component'a ihtiyaç duyarız.

Context.Provider

Context Provider oluşturduğumuz context'i kullanacak componentların erişebilmesini sağlayan, context güncellemelerinde bu componentların yeniden render olmasını sağlayan ve oluşturduğumuz context'ten türeyen bir component'tır. Bu component value isminde bir prop alır ve bu prop ile context değerleri child componentlara aktarılır.

const ToggleContext = React.createContext()

<ToggleContext.Provider value={/* componentlara dağıtılacak değer */}>

💡 Burada Context Provider componentının context'i kullanacak componentların parent'ı olmasına dikkat edilmesi gerekir. Aksi takdirde componentlar context'e erişemezler.

Context.Consumer

Context Consumer componetları Context Provider ile gönderilen context değerlerini kullanacağımız componentlar içinde erişilebilir hale getirir. Bu component value adında parametreye sahip bir fonksiyon alır ve bu fonksiyonun gövdesinde context değerlerine ulaşabilir hale geliriz.

<ToggleContext.Consumer>
  {value => /* Herhangi bir render işlemi */}
</ToggleContext.Consumer>

Bu yöntemi birden fazla context'i kullanan componentlar için önermiyorum. Çünkü birden fazla context'i kullanan component için bu yöntem aşağıdaki gibi çirkin bir görünüme sahip olacaktır.

<ThemeContext.Consumer>
  {theme => (
    <AuthContext.Consumer>
      {user => <div className={`${theme === 'dark' ? 'dark-theme' : ''}`}>{user.name}</div>}
    </AuthContext.Consumer>
  )}
</ThemeContext.Consumer>

Componentların context değerlerine ulaşabilmeleri için diğer yöntemleri inceleyelim. Benim tavsiyem Context Consumer componentlarını kullanmamanız yönünde olacaktır.

Class Based Componentlarda Context Kullanımı

Class based componentlar Component sınıfından türetilir. Bu tip componentlarda context değerlerine ulaşabilmek için static olarak contextType değerini kullanmak istediğimiz context'e set etmemiz gerekir.

import React, { Component } from 'react'

import AuthContext from './context/AuthContext'

class Header extends Component {
  static contextType = AuthContext

  render() {
    const { isLoggedIn, user } = this.context

    return <div>{isLoggedIn ? <span>{user.name}</span> : <button>Giriş Yap</button>}</div>
  }
}

⚠️ Maalesef contextType değerine birden fazla context değişkenini set edemiyoruz. Bu yüzden class based componentlarda çoklu context kullanmak için Context Consumer yöntemine geçmemiz gerekiyor.

Functional Componentlarda Context Kullanımı

React'ın yeni versiyonları ile birlikte functional componentlarda da istediğimiz bir çok işlemi yapabilmeye başladık. Bununla birlikte class based component kullanımı azalıp functional component kullanma oranı büyük ölçüde arttı. Context kullanımı da hem çok rahat hem de birden fazla context kullanmaya izin veriyor olması ile benim en çok kullandığım component tipi olmaya başladı 🎉🎉. Functional componentlarda context değerlerine ulşamak için useContext hook'unu kullanıyoruz. Bu hook parametre olarak oluşturduğumuz context'i alır. Hemen bir önceki örneği functional component kullanarak yapalım.

import React, { useContext } from 'react'

import AuthContext from './context/AuthContext'
import ThemeContext from './context/ThemeContext'

const Header = () => {
  const { isLoggedIn, user } = useContext(AuthContext)
  const { theme } = useContext(ThemeContext)

  return (
    <div className={`${theme === 'dark' ? 'dark-theme' : ''}`}>
      {isLoggedIn ? <span>{user.name}</span> : <button>Giriş Yap</button>}
    </div>
  )
}

useContext ile hem AuthContext hem de ThemeContext contextlerinin değerlerine ulaşabilmiş olduk.

Dilerseniz en başa dönelim ve yaşadığımız problemi context ile çözmeye çalışalım.

Context İle Dark Mode Uygulaması Yapalım

Uygulama için gerekli dosyaları oluşturmakla başlayalım. Önce proje dizininde components/ klasörü altında Header.js, Navbar.js, AppContainer.js ve ThemeSwitcher.js dosyalarını oluşturalım. Ardından context/ klasörü altında ThemeContext.js dosyasını oluşturalım. Son olarak dark mode uygulaması için gereken stilleri yazacağımız styles.css dosyasını oluşturalım. Dosyalarımızı oluşturduğumuza göre uygulama temasının değerini almak ve bu temayı değiştirebilmek için kullanacağımız context'i oluşturalım.

// ./context/ThemeContext.js
import React, { useState, createContext } from 'react'

const ThemeContext = createContext()

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light')

  const toggleTheme = () => {
    if (theme === 'light') {
      setTheme('dark')
    } else {
      setTheme('light')
    }
  }

  const value = {
    theme,
    toggleTheme
  }

  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
}

export default ThemeContext

Context ile işimiz bu kadar. useState kullanarak state'i ve bu state'i değiştirmek için gereken metodu aldık. Daha sonra bu state'i güncelleyen toggleTheme metodunu oluşturduk ve Context Provider componentına hem bu metodu hem de state'i value prop'una verdik.

Şimdi oluşturduğumuz componentların içini doldurmaya başlayabiliriz.

// ./components/Navbar.js
import React from 'react'

const Navbar = () => (
  <nav>
    <a href='/home'>Home</a>
    <a href='/posts'>Posts</a>
    <a href='/videos'>Videos</a>
    <a href='/about'>About</a>
  </nav>
)

export default Navbar

// ./components/Header.js
import React from 'react'

import Navbar from './Navbar'
import ThemeSwitcher from './ThemeSwitcher'

const Header = () => (
  <header>
    <Navbar />
    <ThemeSwitcher />
  </header>
)

export default Header

ThemeSwitcher.js ve AppContainer.js dosyalarında context'i kullanmaya çalışalım.

// ./components/ThemeSwitcher.js
import React, { useContext } from 'react'

import ThemeContext from '../context/ThemeContext'

const ThemeSwitcher = () => {
  const { theme, toggleTheme } = useContext(ThemeContext)

  return <button onClick={toggleTheme}>{theme === 'dark' ? <span>🌞</span> : <span>🌙</span>}</button>
}

export default ThemeSwitcher
// ./components/AppContainer.js
import React, { useContext } from 'react'

import Header from './Header'
import ThemeContext from '../context/ThemeContext'

const AppContainer = () => {
  const { theme } = useContext(ThemeContext)

  return (
    <main className={`${theme === 'dark' ? 'dark-theme' : ''}`}>
      <Header />
      <div>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi quam dolorem inventore molestias quos magnam
        ipsum ipsam voluptatibus? Tenetur laboriosam sint, non ratione est nulla totam culpa deserunt esse ex!
      </div>
    </main>
  )
}

export default AppContainer

Son olarak da App.js dosyasında ThemeProvider ve AppContainer componentını çağıralım.

// App.js
import React from 'react'

import './styles.css'

import AppContainer from './components/AppContainer'
import { ThemeProvider } from './context/ThemeContext'

export default function App() {
  return (
    <ThemeProvider>
      <AppContainer />
    </ThemeProvider>
  )
}

Uygulamamızı stillendirmek için styles.css dosyasını aşağıdaki gibi günceleyelim.

@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');

* {
  box-sizing: border-box;
}

body {
  font-family: 'Open Sans', sans-serif;
  margin: 0;
}

main {
  color: #2e444e;
  height: 100vh;
}

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 30px;
  background-color: #fbfbfb;
  padding-left: 16px;
  padding-right: 16px;
}

header a {
  color: #76848e;
  text-decoration: none;
  padding: 16px;
  display: inline-block;
}

header button {
  background-color: transparent;
  border: none;
}

main.dark-theme {
  background-color: #11181c;
  color: #bfc8ce;
}

main.dark-theme header {
  background-color: #0b1012;
}

main.dark-theme header a {
  color: #bfc8ce;
}

İşte bu kadar dark mode uygulamamızı react context api kullanarak geliştirdik 🎉🎉🎉. Uygulamanın hazır haline aşağıdan ulaşabilirsiniz.

* * *

Yorumlar

Soru, cevap ve destekleriniz için aşağıdan yorum bırakmayı unutmayın.

* * *
Npx Nedir?BEM Metodolojisi Nedir?