fix(translator): suppress react-hooks/set-state-in-effect in deep-link sync useEffects (follow-up GAP-NOVO-4/5)

The useRef-based pattern is required so that a manual user close while
forceOpen stays true does not immediately re-open the accordion on the
next render (see test 'toggle closes accordion again'). The setState
calls inside the false→true guard are an intentional sync of an external
deep-link prop into local component state — a valid use case the
react-hooks/set-state-in-effect heuristic does not recognize, so suppress
it with an inline comment explaining the rationale.

Clarified the comment on the prevForceOpen useRef to point at the
regression it prevents.
This commit is contained in:
diegosouzapw
2026-05-28 17:15:12 -03:00
parent 0d52125ca6
commit 1d62feaf7a
2 changed files with 8 additions and 6 deletions

View File

@@ -216,16 +216,17 @@ export default function CompressionPreviewAccordion({
// Lazy-render guard (D7): track whether the accordion has ever been opened.
const [hasOpened, setHasOpened] = useState(forceOpen);
const [open, setOpen] = useState(forceOpen);
// Track previous forceOpen value to detect false→true transitions only.
// Track previous forceOpen so the effect only reacts to false→true transitions.
// Without this, a manual close while forceOpen stays true would re-open the accordion
// on the very next render (the test "toggle closes accordion again" guards this).
const prevForceOpen = useRef(forceOpen);
// Sync forceOpen changes from parent after mount (deep-link / back-forward navigation).
// Only reacts to a false→true transition so a manual close is not overridden while
// forceOpen remains true (e.g. user toggles closed after a deep-link open).
useEffect(() => {
const prev = prevForceOpen.current;
prevForceOpen.current = Boolean(forceOpen);
if (!prev && forceOpen) {
// eslint-disable-next-line react-hooks/set-state-in-effect -- syncing deep-link prop into local state
setOpen(true);
setHasOpened(true);
onOpenChange?.(true);

View File

@@ -140,16 +140,17 @@ export default function StreamTransformerAccordion({
const [open, setOpen] = useState(Boolean(forceOpen));
// D7 lazy-render guard: once mounted, keep content in DOM.
const [hasOpened, setHasOpened] = useState(Boolean(forceOpen));
// Track previous forceOpen value to detect false→true transitions only.
// Track previous forceOpen so the effect only reacts to false→true transitions.
// Without this, a manual close while forceOpen stays true would re-open the accordion
// on the very next render.
const prevForceOpen = useRef(Boolean(forceOpen));
// Sync forceOpen changes from parent after mount (deep-link / back-forward navigation).
// Only reacts to a false→true transition so a manual close is not overridden while
// forceOpen remains true (e.g. user toggles closed after a deep-link open).
useEffect(() => {
const prev = prevForceOpen.current;
prevForceOpen.current = Boolean(forceOpen);
if (!prev && forceOpen) {
// eslint-disable-next-line react-hooks/set-state-in-effect -- syncing deep-link prop into local state
setOpen(true);
setHasOpened(true);
onOpenChange?.(true);