Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Run a listener-derived state calculation once on mount, not only on future events
When an effect derives UI state from a scroll/resize/mutation listener, call the handler once synchronously right after registering it. Otherwise the derived state stays at its initial value until the first event fires — wrong on reload with restored scroll position, a deep link landing mid-page, or any case where the current state doesn't match a freshly-mounted default.
Bad example
| 1 | useEffect(() => { |
| 2 | const updateCollapsing = () => { |
| 3 | const isSticking = header.getBoundingClientRect().top <= 0; |
| 4 | setCollapsed(isSticking && window.scrollY >= COLLAPSE_DISTANCE); |
| 5 | }; |
| 6 | window.addEventListener('scroll', updateCollapsing, { passive: true }); |
| 7 | return () => window.removeEventListener('scroll', updateCollapsing); |
| 8 | }, []); |
Explanation (EN)
collapsed starts false and only updates the next time 'scroll' fires. On a page reload with a restored scroll position, or a deep link that lands mid-page, no scroll event fires — so the component renders the wrong initial state until the user manually scrolls.
Objašnjenje (HR)
collapsed počinje kao false i ažurira se tek kad se sljedeći put okine 'scroll'. Kod reloada sa sačuvanom scroll pozicijom ili kod deep linka koji vodi na sredinu stranice scroll event se ne okida — pa se komponenta renderira u pogrešnom početnom stanju dok korisnik ručno ne pomakne scroll.
Good example
| 1 | useEffect(() => { |
| 2 | const updateCollapsing = () => { |
| 3 | const isSticking = header.getBoundingClientRect().top <= 0; |
| 4 | setCollapsed(isSticking && window.scrollY >= COLLAPSE_DISTANCE); |
| 5 | }; |
| 6 | updateCollapsing(); |
| 7 | window.addEventListener('scroll', updateCollapsing, { passive: true }); |
| 8 | return () => window.removeEventListener('scroll', updateCollapsing); |
| 9 | }, []); |
Explanation (EN)
Calling updateCollapsing() once right after defining it seeds the state from the actual current scroll/layout position, so the initial render is correct regardless of how the page was entered — reload, deep link, or fresh navigation.
Objašnjenje (HR)
Pozivanjem updateCollapsing() odmah nakon definicije, stanje se inicijalno postavlja prema stvarnoj trenutnoj scroll/layout poziciji, pa je početni render ispravan bez obzira na to kako je stranica otvorena — reload, deep link ili svježa navigacija.