feat(site-finder): debounce weight re-analyze (#201 Phase 2) #215
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#215
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/201-debounce-weights"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
useDebouncedValue<T>(value, delayMs)hook (NEW, 19 LOC) — generic debouncesite-finder/page.tsx: replace directmutateвhandleWeightsChange→pendingWeightsChangestate +useDebouncedValue(pendingWeightsChange, 300)+useEffectдля fire mutate после 300ms тишиныisPending && !!dataRace condition
useMutation(неuseQuery) — last settled mutation wins.dataRef/profileUserIdRefчерез ref предотвращают stale closure без re-register timeout на каждом analysis completion.Test plan
Pre-push checks
Closes #201
Code Review verdict
Summary
frontend/src/hooks/**+frontend/src/app/site-finder/**— ALLOWED for auto-merge per.claude/rules/git-pr.mdCritical issues (BLOCK push)
None.
Minor observations (non-blocking)
useDebouncedValue+delayMsdep (useDebouncedValue.ts:16) — includingdelayMsin deps means a runtime change to the delay resets the timer. Correct for the hook contract (the only caller passes a constant300), just noting.page.tsx:181) — debounce collapses rapid bursts (good), but if user triggers two re-analyzes separated by >300ms, the first request continues. TanStackuseMutation"last settled wins" still applies formutation.data, so the UI converges correctly — но при долгом /analyze (5–10s) теоретически возможно кратко увидеть stale данные от первого request. PR body уже это документирует; current behavior is acceptable.dataRef.current = dataduring render (page.tsx:165, 167) — works (refs are mutable, no concurrent-mode hazard since not used as state-of-truth), но slightly non-idiomatic vsuseEffect(() => { ref.current = data }). Не блокер — комментарий поясняет намерение.weightsChangeInitializedRefblocks first run, и даже если React 19 dev StrictMode double-invokes,if (!debouncedWeightsChange) returnловит второй проход (initialpendingWeightsChange === null). Safe.Positive observations
useDebouncedValue<T>правильно типизирован, cleanupclearTimeoutкорректен, deps массив минимален.pendingWeightsChangestate strict-typed ({ weights: Record<PoiCategoryKey, number>; profileId: number | null } | null) — noany.isPending && !!dataкорректно разделяет initial analyze (CadInput loading) и re-analyze (мелкий hint под панелью весов).eslint-disable-next-lineсопровождён explanation (mutate is stable from useMutation) — соответствует принятой практике.handleAnalyze(первичный analyze) unchanged — debounce затрагивает только weight-change path.Recommended next steps
300в named constWEIGHTS_DEBOUNCE_MSрядом с компонентом, но это micro-nit.