# Vuex ile Vue uygulamasında durum yönetimi

> Vuex ile bileşenler arası paylaşılan durumu tek bir kaynakta toplamak; state, mutation, action ve getter kavramları.

- Yayın: 2017-04-02
- Kategori: Arayüz
- Etiketler: Vue.js
- Okuma süresi: 3 dk okuma
- Kaynak: https://www.muhammetsafak.com.tr/blog/vuex-ile-vue-uygulamasinda-durum-yonetimi/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
[Vue ile bileşen tabanlı arayüz yazmaya başladığımda](/blog/vue-js-ile-ilk-reaktif-bilesen) bir süre `$emit` ve `props` ile idare ettim. Küçük uygulamalarda bu yeterli. Ama bileşen sayısı artınca ve birbirleriyle ilişkisi olmayan kardeş bileşenler aynı veriyi paylaşmak zorunda kalınca bu yol çıkmaza giriyor. Kimin kime ne gönderdiğini takip etmek güçleşiyor.

**Vuex**, Vue için geliştirilen merkezi state management kütüphanesi. Paylaşılan veriyi tek bir yerde toplamak ve tüm bileşenlerin aynı kaynaktan okuyup aynı kurallara göre güncellemesini sağlamak için tasarlanmış.

## Temel yapı

Vuex'in dört ana kavramı var:

- **State:** Uygulamanın tek doğru kaynağı. Tüm bileşenler buradan okur.
- **Mutation:** State'i değiştirmenin tek yolu. Senkron olmalıdır.
- **Action:** Asenkron işlemleri barındırır; bitince mutation çağırır.
- **Getter:** State'ten türetilmiş, hesaplanmış değerler.

Basit bir alışveriş sepeti örneği:

```javascript
const store = new Vuex.Store({
  state: {
    cartItems: []
  },
  mutations: {
    ADD_ITEM(state, item) {
      state.cartItems.push(item);
    },
    REMOVE_ITEM(state, itemId) {
      state.cartItems = state.cartItems.filter(i => i.id !== itemId);
    }
  },
  actions: {
    addToCart({ commit }, item) {
      // Asenkron bir kontrol yapılabilir; örn. stok sorgusu
      commit('ADD_ITEM', item);
    }
  },
  getters: {
    cartTotal(state) {
      return state.cartItems.reduce((sum, item) => sum + item.price, 0);
    }
  }
});
```

## Bileşenden store'a erişim

Store Vue örneğine enjekte edildiğinde tüm bileşenlerden `this.$store` ile erişilebilir:

```javascript
// Ürün bileşeni
methods: {
  addProduct(product) {
    this.$store.dispatch('addToCart', product);
  }
},
computed: {
  total() {
    return this.$store.getters.cartTotal;
  }
}
```

Sepet bileşeni ve ürün listesi bileşeni tamamen farklı yerlerde olabilir; ikisi de aynı store'dan okuduğu için senkronizasyon kendiliğinden sağlanıyor.

## mapState ve mapGetters

Her seferinde `this.$store.state.cartItems` yazmak yorucu. Vuex'in yardımcı fonksiyonları bunu kısaltıyor:

```javascript
import { mapState, mapGetters } from 'vuex';

computed: {
  ...mapState(['cartItems']),
  ...mapGetters(['cartTotal'])
}
```

Bu sayede `this.cartItems` ve `this.cartTotal` direkt kullanılabilir hale geliyor.

## Mutation ve action arasındaki fark

Bu ayrımı ilk gördüğümde gereksiz gibi geldi: neden hem mutation hem action? Cevabı asenkron işlemlerde yatıyor.

Mutation daima **senkron** çalışmalıdır. Bu kural Vuex DevTools'un state değişikliklerini doğru sırayla kaydetmesini sağlıyor. Senkron olmayan bir mutation, araçların ne zaman ne değiştiğini takip etmesini zorlaştırır.

Action ise asenkron işleri üstlenir. API çağrısı yapılır, sonuç gelir, mutation commit edilir:

```javascript
actions: {
  async fetchCart({ commit }, userId) {
    const response = await fetch(`/api/cart/${userId}`);
    const items = await response.json();
    commit('SET_CART', items);
  }
}
```

Bu ayrım biraz fazla katı görünebilir; ama zamanla takdir ediyorsunuz. State değişikliği her zaman mutation üzerinden geçtiği için, her değişikliğin neden ve ne zaman olduğunu DevTools'ta görmek mümkün oluyor.

## Ne zaman Vuex gerekiyor?

Her Vue projesine Vuex eklemek gerekmiyor. Kısa vadeli, birkaç bileşenden oluşan bir form arayüzü için `props` ve `$emit` yeterli. Şu koşullar varsa Vuex düşünmeye değer:

- Birden fazla, birbirine bağlı olmayan bileşen aynı veriyi okuyor.
- Sunucudan çekilen veri uygulamanın birçok yerinde kullanılıyor.
- Bir kullanıcı eylemi birden çok bileşeni etkiliyor.

Bu sorulardan ikisi veya üçü "evet" ise, store kurmak başlangıçta biraz ek iş gerektiriyor ama zamanla kazancını veriyor.

Bir uyarı: store'u erken ve her şey için kullanmaya başlamak da bir tuzak. Yerel bir bileşen state'i olarak kalması gereken şeyi (örneğin bir formun `isLoading` durumu) store'a taşımak kodu gereksiz yere karmaşıklaştırıyor. Kural basit: veri yalnızca bir bileşeni ilgilendiriyorsa yerel bırakın, birden fazlasını ilgilendiriyorsa store'a taşıyın.

## Modüller ile store'u bölmek

Uygulama büyüdükçe tek bir store dosyası şişiyor. Vuex modülleri ile store parçalara ayrılabiliyor:

```javascript
const cartModule = {
  namespaced: true,
  state: { items: [] },
  mutations: { /* ... */ },
  actions: { /* ... */ }
};

const store = new Vuex.Store({
  modules: {
    cart: cartModule,
    user: userModule
  }
});
```

`namespaced: true` ile erişim `this.$store.dispatch('cart/addToCart', item)` biçimine dönüşüyor. Bu ayarı açmadan modüller birbirinin mutation isimlerini ezebiliyor — nadir ama sinir bozucu bir hata.

## Vuex DevTools ile izleme

Vue DevTools tarayıcı eklentisi Vuex ile birlikte çok daha kullanışlı hale geliyor. Her mutation'ın ne zaman, hangi veriyle tetiklendiğini time-travel debugging ile görmek mümkün. Arayüzdeki tuhaf davranışların kaynağını bulmak bu araçla gerçekten kolaylaşıyor.

Vuex'i ilk kullandığımda biraz aşırı mühendislik gibi geldi. Sonradan tek bir `cartItems` dizisinin beş farklı bileşene `$emit` zinciriyle ulaşmaya çalıştığım kodu hatırladım — ve store'un neden var olduğunu anladım.
