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).
Extract imperative DOM logic into custom hooks
Move direct DOM manipulations like scroll locking or event listeners into named custom hooks to separate side effects from UI logic.
Bad example
| 1 | const Modal = ({ isOpen, children }: { isOpen: boolean; children: React.ReactNode }) => { |
| 2 | // Bad: Imperative DOM logic mixed with UI rendering |
| 3 | useEffect(() => { |
| 4 | if (isOpen) { |
| 5 | document.body.style.overflow = 'hidden'; |
| 6 | } else { |
| 7 | document.body.style.overflow = ''; |
| 8 | } |
| 9 | return () => { |
| 10 | document.body.style.overflow = ''; |
| 11 | }; |
| 12 | }, [isOpen]); |
| 13 |
|
| 14 | if (!isOpen) return null; |
| 15 | return <div className="modal">{children}</div>; |
| 16 | }; |
Explanation (EN)
The component directly manipulates the global `document` object inside `useEffect`. This mixes imperative side effects with declarative UI code, making the component harder to read and the logic impossible to reuse.
Objašnjenje (HR)
Komponenta izravno manipulira globalnim `document` objektom unutar `useEffect`. Ovo miješa imperativne nuspojave s deklarativnim UI kodom, čineći komponentu težom za čitanje, a logiku nemogućom za ponovnu upotrebu.
Good example
| 1 | // hooks/useLockBodyScroll.ts |
| 2 | function useLockBodyScroll(isLocked: boolean) { |
| 3 | useEffect(() => { |
| 4 | document.body.style.overflow = isLocked ? 'hidden' : ''; |
| 5 | return () => { |
| 6 | document.body.style.overflow = ''; |
| 7 | }; |
| 8 | }, [isLocked]); |
| 9 | } |
| 10 |
|
| 11 | // Modal.tsx |
| 12 | const Modal = ({ isOpen, children }: { isOpen: boolean; children: React.ReactNode }) => { |
| 13 | // Good: Logic is encapsulated and descriptive |
| 14 | useLockBodyScroll(isOpen); |
| 15 |
|
| 16 | if (!isOpen) return null; |
| 17 | return <div className="modal">{children}</div>; |
| 18 | }; |
Explanation (EN)
The DOM manipulation logic is extracted into a named custom hook `useLockBodyScroll`. This makes the `Modal` component cleaner, self-documenting, and allows the scroll-locking behavior to be reused in other components (e.g., Drawers or MobileMenus).
Objašnjenje (HR)
Logika manipulacije DOM-om izvučena je u imenovani custom hook `useLockBodyScroll`. To čini `Modal` komponentu čišćom i jasnijom, te omogućuje ponovnu upotrebu logike zaključavanja skrolanja u drugim komponentama (npr. Drawer ili MobileMenu).