ViraStack
  • Akademi
  • Hakkında
  • Destek
Genel BakışKaynaklarKatkı
ViraStack

Frontend ekosistemi: ücretsiz rehberler, proje şablonları, AI kuralları ve form bileşenleri.

Ürünler

  • ViraStack Start
  • ViraStack AI
  • ViraStack Mask
  • ViraStack Password
  • ViraStack Guide

Keşfet

  • Akademi
  • Hakkında
  • Marka
  • Destek

© 2026 ViraStack.

Açık kaynak · MIT

Zustand

03 / 09

Selector ve yeniden render

Selector'ın nasıl karşılaştırıldığı, atomik selector, useShallow, v5'teki sonsuz döngü hatası, türetilmiş değer ve yalnızca custom hook export etmek.

Selector Nasıl Karşılaştırılır

Store her değiştiğinde Zustand, component'teki selector'ı yeniden çalıştırır ve sonucu bir öncekiyle Object.is ile karşılaştırır. Sonuç farklıysa component render olur, aynıysa olmaz.12

const count = useCartStore((state) => state.items.length);

Burada sonuç bir sayıdır. Sepet kapanıp açılsa da sayı aynı kaldığı sürece component render olmaz. Selector'ın döndürdüğü değer ne kadar küçükse component o kadar az render olur.

Bütün Store'a Abone Olmak

Selector vermezseniz hook bütün state'i döndürür. Bu, store'daki her değişiklikte render demektir:3

// ❌ Yalnızca items'ı kullanıyor ama isOpen değişince de render olur
const { items } = useCartStore();

TkDodo bu yüzden selector'ları zorunlu sayar: store'da tek bir değer olsa bile bir selector yazın.3

Atomik Selector

Her değer için ayrı bir selector yazmak en basit ve en güvenli yoldur:3

const items = useCartStore((state) => state.items);
const isOpen = useCartStore((state) => state.isOpen);

Her satır kendi değerine abone olur. TkDodo'nun tercihi budur: bir component iki değere ihtiyaç duyuyorsa iki selector kullanır.3

Nesne Döndüren Selector

Birden fazla değeri tek seferde almak için nesne döndürmek caziptir. Ama selector her çalıştığında yeni bir nesne üretir; Object.is iki nesneyi hiçbir zaman eşit saymaz.1

// ❌ Her seferinde yeni nesne
const { items, isOpen } = useCartStore((state) => ({
  items: state.items,
  isOpen: state.isOpen,
}));

v4'te bu kod yalnızca gereksiz render'a yol açıyordu. v5 React'in davranışına uyduğu için sonuç daha ağırdır: React, değerin her okumada değiştiğini görür ve sayfa "Maximum update depth exceeded" hatasıyla durur.4 Konsolda öncesinde "The result of getSnapshot should be cached" uyarısı çıkar.

Çözüm useShallow'dur. Selector'ı sarar ve sonucu sığ karşılaştırır: nesnenin ya da dizinin her elemanı Object.is ile aynıysa önceki referansı döndürür.24

import { useShallow } from "zustand/react/shallow";

const { items, isOpen } = useCartStore(
  useShallow((state) => ({ items: state.items, isOpen: state.isOpen })),
);

// Dizi döndürmek de aynı
const [items, isOpen] = useCartStore(useShallow((state) => [state.items, state.isOpen]));

useShallow hem zustand/react/shallow'dan hem zustand/shallow'dan import edilebilir.4

Uyarı

Varsayılan değeri selector içinde üretmek de aynı hatayı verir. state.action ?? (() => {}) her seferinde yeni bir fonksiyon döndürür. Varsayılanı selector'ın dışında bir sabite koyun.4

Türetilmiş Değer

Hesaplanabilen bir değeri store'a ayrıca yazmayın; selector'da hesaplayın.5 Sonuç bir sayı ya da metinse ek bir şey gerekmez:

const total = useCartStore((state) =>
  state.items.reduce((sum, item) => sum + item.price * item.quantity, 0),
);

Sonuç yeni bir dizi ise (filter, map, Object.keys) her seferinde yeni referans üretir. useShallow ile sarın; dizi elemanları aynı kaldıkça component render olmaz:1

const ids = useCartStore(useShallow((state) => state.items.map((item) => item.id)));

Bu kalıp listeler için kullanışlıdır. Liste component'i yalnızca id'leri okur; her satır kendi kaydını id ile seçer. Bir ürünün adedi değişince yalnızca o satır render olur.

Yalnızca Custom Hook Export Edin

TkDodo'nun bir numaralı önerisi: store hook'unu dışarı açmayın, her değer için bir custom hook export edin.3

// Export edilmiyor: kimse bütün store'a abone olamaz
const useCartStore = create<CartState>()((set) => ({
  /* ... */
}));

export const useCartItems = () => useCartStore((state) => state.items);
export const useCartCount = () =>
  useCartStore((state) => state.items.reduce((sum, item) => sum + item.quantity, 0));
export const useIsCartOpen = () => useCartStore((state) => state.isOpen);

Kullanan component selector yazmak zorunda kalmaz, yanlışlıkla bütün store'a abone olamaz, store'un iç yapısı değişince yalnızca bu dosya değişir. Action'ları dışarı açmanın yolu Action ve güncelleme dersinde.

Kendin Dene

  • Üç component yazın: biri bütün store'u, biri atomik selector'ı, biri useShallow ile nesne okusun. Her birine console.log("render") koyup store'da ilgisiz bir alanı değiştirin.

  • useShallow'u kaldırıp nesne döndüren selector'ı çalıştırın. Konsolda hangi uyarı ve hata çıkıyor?

  • Sepet listesini önce items'ı okuyarak, sonra yalnızca id'leri okuyup satırlara böldükten sonra render edin. Bir ürünün adedini değiştirince kaç satır render oluyor?

Detaylı Öğrenmek İçin

  • Yazı: TkDodo, "Working with Zustand"; custom hook export etmek, atomik selector ve action'ları ayırmak.3

  • Resmi doküman: "Prevent rerenders with useShallow"; türetilmiş dizide useShallow örneği.1

  • Resmi doküman: "How to Migrate to v5 from v4"; kararlı selector çıktısı şartı ve sonsuz döngü örnekleri.4

Kaynaklar

  • 1DokümanZustand: Prevent rerenders with useShallow

    `Object.is` karşılaştırması ve türetilmiş dizide gereksiz render.

  • 2DokümanZustand: useShallow

    Selector'ı sığ karşılaştırmayla memoize etmek.

  • 3MakaleTkDodo

    : — 20 Kasım 2022. Yalnızca custom hook export etmek, atomik selector ve bütün store'a abone olmanın maliyeti.

  • 4DokümanZustand: How to Migrate to v5 from v4

    Kararlı selector çıktısı şartı, "Maximum update depth exceeded" ve `useShallow` ile çözüm.

  • 5DokümanZustand: Beginner TypeScript Guide

    Birden fazla selector ve selector'da türetilmiş state.

Bu sayfayı düzenle

Son güncelleme tarihi: 21 Ekim 2026

Önceki adım02. İlk storeSonraki adım04. Action ve güncelleme