Photo by Hans-Jurgen Mager on Unsplash

Zustand

Zustand ile Re-Render Senaryoları ve Optimizasyon Yöntemleri — 1

State Management kütüphaneleri doğru kullanmadığımız taktirde bileşenlerin re-render , yani tekrar tekrar render edildiğini görürüz. Bu durumu nasıl engelleyebiliriz ?

Frontend Development With JS
4 min readNov 20, 2023

--

İster Zustand olsun ister diğer ClientState veya ServerState kütüphaneleri olsun state’lerdeki güncellemeler bileşenlerin re-render edilmesine neden olur.

Bileşenlerin re-render için harcadığı süreler ms ise neden bu re-render konusuna kafa yoruyoruz ?

Nedeni re-render aynı zamanda bileşenlerde tekrardan fetching / subscribe işlemlerini başlatır. Bu işlemler network veya web-api kullanımından dolayı sistemi çok yavaşlatır ayrıca gereksiz bir kaynak kullanımına ve sistemin karmaşıklaşarak takip edilemez hale gelmesine neden olur.

Bileşenlerin gerekli olmadığı sürece tekrar render edilmemesi en önemli amaçlarınızdan birisi olmalıdır.

Bu konuyu daha önceden Client State Management kütüphanelerinde üzerinde durmuştum.

Tüm örneklerde Counter objelerini tetikleyen yer ile gösteren yer arasında uzak ağaç düğümleri oluşturup re-render durumlarını analiz etmiştik.

Örneğin linkteki bu örnekte.

  • Inc1 düğmesine bastığınızda sadece C1 bileşeni
  • Inc2 düğmesine bastığımızda sadece C2 bileşeni render edilir.
https://onurdayibasi.dev/state-zustand

Basit bir number değeri için bu senaryoda re-rendering olayını engelledik fakat daha kompleks yapılarda object ve array olduğu yapılarda bu re-rendering olayını basitçe halledebilecek miyiz ?

Tüm yukarıdaki örneklere ait kodları LearnReactUI.dev üzerinden edinebilirsiniz.

Default, Atomic ve Shallow

Senaryomuzu biraz daha kompleks hale getirelim. Aşağıdaki örnekte şunu yapmaya çalışıyoruz

  • Counter1 → Comp1, Comp2, Comp3 Re-Render etmesini istiyoruz
  • Counter2 → Comp1, Comp2 Re-Render etmesini Comp3 ilişkisi olmamasını istiyoruz
https://onurdayibasi.dev/state-zustand2

Default

Store kısmında normal store oluşturuyoruz

import {create} from 'zustand';

export const useSampleStore = create((set) => ({
count1: 0,
count2: 0,
increase1: () => set((state) => ({count1: state.count1 + 1})),
increase2: () => set((state) => ({count2: state.count2 + 1})),
}));

Client Comp3 kısmında count1 useSample

import React from 'react';

import {useSampleStore} from './store';

function Comp3() {
const {count1} = useSampleStore();
console.log('Comp3 render');
return (
<>
<div>Comp3</div>
<span>{new Date().getMilliseconds()}</span>
<div>count 1 :{count1}</div>
</>
);
}

export default Comp3;

Default yöntemde sistem istediğimiz gibi çalışmıyor. Çünkü Counter2 → Comp3 render tetikliyor. Biz bunun olmasını isteMiyoruz

Peki bunun için ne yapabiliriz.

Atomic Yöntem

Store Atomic şekilde kullanıcak hale getirmek Working with Zustand bu konunun detayını bulabilirsiniz.

Redux ta kullandığımız connect APIsi üzerinden mapStateToProps and mapDispatchToProps ile store getirdiğimiz yapıya dönüştürerek bunu başarabiliriz.

Redux konusunu detaylı kullanımını anlamak için bu blog yazısını okuyabilirsiniz.

mapStateToProps, mapDispatchToProps

Peki bunu Zustand atomic olarak nasıl gerçekleştirebiliriz ?

Store aşağıdaki şekilde birçok alt kırılımlı atomic yapıda useCount1, useCount2 haline getirebiliriz.

import {create} from 'zustand';

export const useSampleStore = create((set) => ({
count1: 0,
count2: 0,
increase1: () => set((state) => ({count1: state.count1 + 1})),
increase2: () => set((state) => ({count2: state.count2 + 1})),
}));


export const useCount1 = () => useSampleStore((state) => state.count1);
export const useCount2 = () => useSampleStore((state) => state.count2);

Bu durumda Comp3 sadece useCount1 ile atomic bağlantı kuracağı için,

import React from 'react';

import {useCount1} from './store';

function Comp3Atomic() {
const count1 = useCount1();
console.log('Comp3 render');
return (
<>
<div>Comp3 Atomic</div>
<div>count 1 :{count1}</div>
</>
);
}

export default Comp3Atomic;

Atomic opsiyonunu şeçtiğinizde Counter2 → Comp3 re-render etmediğini görürüz. Bu istediğimiz bir sonuçtur.

Shallow Yöntem

useShallow store değerleri arasında compare yapıp bileşende kullanılan store proplarının re-render tetiklemesi engellenir.

import {create} from 'zustand';
import {useShallow} from 'zustand/react/shallow';

export const useSampleStore = create((set) => ({
count1: 0,
count2: 0,
increase1: () => set((state) => ({count1: state.count1 + 1})),
increase2: () => set((state) => ({count2: state.count2 + 1})),
}));


export const useSampleStore2 = () =>
useSampleStore(
useShallow((state) => ({
count1: state.count1,
count2: state.count2,
increase1: state.increase1,
increase2: state.increase2,
})),
);

Aşağıdaki örnekte görüleceği üzere useShallow türeyen useSampleStore2 den alınan count1 herhangi bir değer değişikliği olmadığı için Comp3 için re-render gerçekleşmez.

import React from 'react';

import {useSampleStore2} from './store';

function Comp3Shallow() {
const {count1} = useSampleStore2();
console.log('Comp3 render');
return (
<>
<div>Comp3 Shallow</div>
<div>count 1 :{count1}</div>
</>
);
}

export default Comp3Shallow;

Referanslar

Okumaya Devam Et 😃

Bu yazının devamı veya yazı grubundaki diğer yazılara (React State Management) erişmek için bu linke tıklayabilirsiniz.

--

--