React Hooks ve Context.Provider ile State Yönetimi

React’ın 16.7. sürümünün alpha versiyonuyla birlikte başlayan Hooks geliştirmesi 16.8. sürüm ile birlikte stabil hale geldi. Bu yeni sürümle birlikte Hooks geliştirmesi Context’lerle birlikte global state yönetimine farklı bir çözüm getiriyor.

React Hooks hakkında gündemi çok fazla takip edemediğinizi düşünüyor ve ön bilgi edinmek istiyorsanız sunumu izleyebilir ya da dökümanlarına göz gezdirebilirsiniz. Hooks sunumda da gösterildiği üzere beraberinde useEffect, useState gibi birçok yöntemle birlikte geliyor.

Bu aşamada yazılımcılar açısından bakıldığında en önemli tarafının, fazladan bir kütüphane bağımlılığı olmadan state yönetimini halledebilmek olduğunu söyleyebiliriz. Redux’un uzun zamandır bu alanda büyük bir yükü üstlendiğini belirterek, artık benzer bir söz dizimi ile daha temiz bir yaklaşımla, benim fikrime göre, state yönetimini kotarmak mümkün olacak gibi duruyor.

Bir dipnot belirtelim. Daha önce Redux, Mobx ve Vuex üzerine giriş niteliğinde bir şeyler yazmış bulunmaktayım. O yazıları da inceleyip projelerinizde ne tarz bir yapı kullanmak istediğinize daha sağlıklı karar verebilirsiniz.

Medium’da React üzerine anlattığım yazılarda geçen örnekleri barındıran bir repo oluşturdum. Örnekleri mümkün olduğunca orada tutmaya çalışacağım. Geliştirici ortamını kurmak ve çalışan uygulamayı yerel ortamınıza çekmek için repoyu kullanabilirsiniz.

Repoyu yerele çektiğinizde uygulamanın parcel ile ayağa kaldırıldığını görebilirsiniz. “npm install” ile gerekli paketleri kurduktan sonra “npm start” ile 5000 portunda uygulamayı görebilirsiniz.

İlk bakışta daha önce aşina olanlar için Redux’a benzer bir yapı olduğunu söylemek mümkün. Burada kurguladığımız yapı şu şekilde oluşuyor:

store içerisinde uygulamanın state’lerini taşıyan context’leri oluşturuyoruz. Bu oluşturduğumuz Context’leri sonrasında gerekli component’larda React’ın sunduğu useContext metoduyla birlikte kullanacağız. Reducer’lar ile context’lerin içerisindeki verileri değiştirmeyi amaçlıyoruz. Reducer’larla iletişime geçerken, tetiklemek amacıyla ya da parametre gönderebilmek için, action’ları Context içerisinde tanımladığımız set metoduyla birlikte kullanıyoruz. Ben repo’da dispatch olarak tanımladım. Alışkanlık olarak setState de denilebilir.

Veriyi tutacağımız, dağıtacağımız ve değiştirmemize olanak sağlayan metodu barındıran Store.js’i oluşturalım.

import React, { createContext, useReducer } from 'react'
import { node } from 'prop-types'
// load reducers
import userReducer from '../reducer/userReducer'
// craete initial payload
const users = [
  {
    name: 'Algun',
    title: 'developer'
  },
  {
    name: 'Ahmet',
    title: 'developer'
  }
]
// create context
export const MainContext = createContext(users);
// create Store
function Store({ children }) {
  const [state, dispatch] = useReducer(userReducer, users);
  const value = { state, dispatch };
  return (
    <MainContext.Provider value={value}>
      {children}
    </MainContext.Provider>
  )
}
Store.defaultProps = {
  children: null
}
Store.propTypes = {
  children: node
}

export default Store;

Sırasıyla anlatırsak:

Store’a React’ı ve daha sonrasında kullanacağımız useReducer ve createContext metodlarını dahıl ediyoruz.

users adında bir array oluşturduk. Bu array, herhangi bir servisten de gelebilir. Burada static olarak oluşturduk. İçerisinde barındırdığı 2 kişi bilgisini alt compenant’lara dağıtacağız ve yine alt component’lardan değiştirmeyi deneyeceğiz.

createContext ile MainContext’i oluşturarak, daha önce yarattığımız users array’ini inital value olarak Context’e verdik.

Sonrasında yapacağımız uygulamamızın içerisinde render olabileceği bir Store oluşturmak. Bir kapsayıcı mantığıyla güdüldüğünü düşünebilirsiniz. children props’u sonrasında uygulamamızın diğer component’larını render edeceğimiz yer. Store’u export etmeden önce de children props için default value atayıp tipini belirliyoruz.

...
const [state, dispatch] = useReducer(userReducer, users);
const value = { state, dispatch };
...

Bu kısım biraz manasız gözükse de yapılan iş es6'nın array destructuing özelliğini kullanmak. Bir göz atarak yapılan işi daha anlaşılır kılabilirsiniz.

useReducer ile userReducer adında oluşturduğumuz ve uygulamanın en başında import ettiğimiz reducer’i ve initial state’i state ve dispatch değerlerine atayıp Provider’a gönderiyoruz.

...
return
(
<MainContext.Provider value={value}>
  {children}
</MainContext.Provider>
)
...

Böylelikte uygulamamız için tematik bir state oluşturup Provider’in kapsayıcılığı altında render olan component’larda bu state’i kullanılabilir kılıyoruz.

Oluşturduğumuz userReducer ise;

export default function userReducer(state, action) {
  switch (action.type) {
    case 'updateName':
      state[0].name = 'Bekir'
      return [...state]
    default:
      return state
  }
}

Kendisini dispatch ile tetiklerken göndereceğimiz objenin içindeki type key’ine göre uygulayacağı yöntemi seçip context içerisindeki state’i güncelleyecektir. child component içerisinde updateName type’ini tetiklediğimizde, initial state’i alıp, ilk objesinin içerisindeki name’i Bekir olarak değiştirmesini söylemiş oluyoruz. Yaptığımız her şey statik olmakla birlikte action’larla parametreler taşıypı dinamik olarak da state’lerinizi yönetebilirsiniz.

Şimdi yukarıda bahsettiğimiz işlemi nasıl yaptığımıza bakarsak:

import React from 'react'
// load store
import Store from './store/Store';
// load components
import List from './components/List'
import Button from './components/Button'
const App = () => (
  <Store>
    <List />
    <Button />
  </Store>
)
export default App

App component’ı oluşturup Store’un içerisinde child component’ları döndürüyoruz. Burada yaptığımız basir bir functional component oluşturup içerisinde Store ve alt component’ları render etmek.

Alt component’larda ise React içerisinde çağıracağımız useContext metoduna parametre olarak vereceğimiz MainContext’den iki şey çağırabiliyor olacağız. Bunlardan biri state, diğeri ise state’i değiştirebilmek için kullanacağımız dispatch.

Gördüğünüz üzere List ve Button olmak üzere iki component var. List component’inda herhangi bir müdahale yapmada sadece var olan state’i render’layacağız. State de bir array olduğu için jsx içerisinde .map yöntemi ile loop’la alıp içerisindeki değerleri return edeceğiz.

import React, { useContext } from 'react'
// load main context
import { MainContext } from '../store/Store'
const List = () => {
const { state } = useContext(MainContext)
return (
    <div>
      <ul>
        {
          state.map((item, index) => {
            const key = index.toString()
            return (
              <li
                key={key}
              >
                <h1>{item.name}</h1>
                <h5>{item.title}</h5>
              </li>
            )
          })
        }
      </ul>
    </div>
  )
}

export default List

MainContext içerisinden state’i kullanarak liste görünümünü oluşturduk.

Button içerisinde ise MainContext içerisinden dispatch metodunu çekeceğiz ve state’i güncellemek için reducer’a erişmek için kullanacağız.

import React, { useContext } from 'react'
// load main context
import { MainContext } from '../store/Store'
// load action
import { updateName } from '../actions/updateName'
const Button = () => {
const { dispatch } = useContext(MainContext)
return (
    <div>
      <button
        type="button"
        onClick={() => dispatch(updateName())}
      >
        Update
      </button>
    </div>
  )
}
export default Button

Yaptığımız şey button’a kullanıcı tıkladığında MainContext’ten çektiğimiz dispatch’i updateName() isimli action ile birlikte çalıştırmak. Böylelikle reducer’a bir type göndermiş oluyoruz ve reducer daha önce oluşturduğumuz yönergeler doğrultusunda state’i güncellemiş oluyor.

// actions/updateName.js
export
const updateName = () => ({ type: 'updateName' })

Sadece type gönderdiğimiz için actions burada basit bir şekilde yazıldı. İsterseniz import etmek yerine doğrudan component içerisinde yazabilirsiniz.


Reach Hooks ilk duyurulduğundan bu yana ciddi bir etki yarattı. Twitter’da ya da benzeri mecralarda konuşulan edilen şeyler hep stabil olmasının heyecanlı bekleyişi üzerineydi. Görünüyor ki yeni geliştirilen uygulamalarda ya da varolan uygulamaların yeni geliştirilen kısımlarında hooks’u biraz daha görür olacağız.

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