Proje: Okul.com.tr CRM · Hub: Okul.com.tr CRM — Conventions

Kural

Form alanlarındaki state değişiklikleri tetiklenecek debounce’lu bir API hesaplaması varsa (ör. /admin/checkouts/calculate), scheduleCalculate() çağrısını handler içinden yapma — ilgili state’leri izleyen bir useEffect kullan.

// YANLIŞ — stale closure, ilk değişiklikte API çağrılmaz
const handleProductChange = (idx, val) => {
    updateRow(idx, {product_price_id: val, unit_price: ...});
    scheduleCalculate(); // setState async; rows snapshot'ı henüz güncel değil
};
 
// DOĞRU — commit sonrası tetikleniyor
useEffect(() => {
    scheduleCalculate();
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, [rows, customer, campaignId, isCashPayment]);

Why

setState asenkron. Handler içinde setRows(...) sonrası aynı tick’te scheduleCalculate() çağrılırsa, setTimeout callback’i o handler’ın render’ından kapanışa giren rows’u görüyor — yeni seçilen değer henüz commit olmamış. Sonuç: collectKnownProducts() boş döner, runCalculate API çağrısını skip eder, özet 0,00 ₺‘de kalır.

useCallback’lenmiş handler’lar (deps eksik tutulmuş) aynı sorunu kalıcı hale getirir: ilk render’ın closure’una sıkışırlar.

How to Apply

  • Hesaplama state’e bağımlıysa, ilgili state’leri useEffect deps’ine koy
  • Handler’lar sadece state’i güncellesin; hesaplamayı tetiklemesin
  • Debounce timer (useRef) hâlâ effect içinde yönetilebilir
  • Reference uygulama: src/pages/checkouts/basket.tsxuseEffect [rows, customer, campaignId, isCashPayment] izliyor

Tetikleyen İncident

/sales/checkouts/basket özet kısmı 0,00 ₺‘de kalıyordu; calculate endpoint’i çağrılmıyordu çünkü scheduleCalculate handler’lardan eski rows ile çağrılıyordu.