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).
Don't return cleanup from a ref callback; use useRef + useEffect
Ref callbacks ignore returned cleanup functions, so listeners/subscriptions attached inside them leak. Hold the node in a ref and set up/tear down the subscription in a useEffect.
Bad example
| 1 | <my-widget |
| 2 | ref={(node) => { |
| 3 | const controller = new AbortController(); |
| 4 | node?.addEventListener('empty', onEmpty, { signal: controller.signal }); |
| 5 | return () => controller.abort(); // ignored by React → listener leaks |
| 6 | }} |
| 7 | /> |
Explanation (EN)
React invokes the ref callback with the node on mount and null on unmount but discards its return value, so controller.abort() is never called and the listener leaks.
Objašnjenje (HR)
React poziva ref callback s cvorom pri montiranju i s null pri demontiranju, ali odbacuje povratnu vrijednost, pa se controller.abort() nikad ne pozove i listener cijeli.
Good example
| 1 | const widgetRef = useRef<HTMLElement>(null); |
| 2 |
|
| 3 | useEffect(() => { |
| 4 | const node = widgetRef.current; |
| 5 | if (!node) return; |
| 6 |
|
| 7 | const controller = new AbortController(); |
| 8 | node.addEventListener('empty', onEmpty, { signal: controller.signal }); |
| 9 |
|
| 10 | return () => controller.abort(); |
| 11 | }, []); |
| 12 |
|
| 13 | return <my-widget ref={widgetRef} />; |
Explanation (EN)
The node is held in a ref and the listener is attached in useEffect, whose returned cleanup actually runs on unmount, properly removing the listener.
Objašnjenje (HR)
Cvor se drzi u refu, a listener se dodaje u useEffectu cija se povratna cistka stvarno izvrsi pri demontiranju i ispravno uklanja listener.
Exceptions / Tradeoffs (EN)
React 19+ does support returning a cleanup function from a ref callback; even so, useRef + useEffect is clearer for non-trivial subscriptions.
Iznimke / Tradeoffi (HR)
React 19+ podrzava vracanje cistke iz ref callbacka; svejedno je useRef + useEffect citljiviji za netrivijalne pretplate.