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 ?
İ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.
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
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.
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.