[7f87f002] Audit and resolve eslint-disable in journals-view.tsx (#739) (#740)

* [7f87f002] fix(panel): remove eslint-disable in journals-view mount effect

Replace the empty-deps + eslint-disable-next-line react-hooks/exhaustive-deps
mount-only localStorage-restore effect with a useRef mount-guard, so the
deps array can honestly list router/searchParams/urlAgentId/urlType/urlTask
while still only running the restore once per mount. Adds regression tests
proving the restore never re-fires after a later filter clear, which a
naive "just add the deps" fix would have broken.

* [7f87f002] docs(frontend): document journals-view mount-guard eslint-disable fix

---------

Co-authored-by: roboco-app[bot] <302741806+roboco-app[bot]@users.noreply.github.com>
Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech>
Co-authored-by: Frontend Documenter <fe-doc@roboco.tech>
This commit is contained in:
roboco-app[bot]
2026-07-30 19:46:01 +00:00
committed by GitHub
co-authored by roboco-app[bot] <302741806+roboco-app[bot]@users.noreply.github.com> Frontend Developer 1 Frontend Documenter
parent 9f07183b01
commit 424c7a0e59
3 changed files with 136 additions and 4 deletions
+23
View File
@@ -138,3 +138,26 @@ export function TaskDetail({ taskId }: { taskId: string | undefined }) {
```
No manual guard is needed before calling the hook — the `enabled: !!taskId` guard is built in and prevents wasted API calls and race conditions.
## Mount-only effect audit: eslint-disable in `journals-view.tsx`
Sentinel's `no_lint_suppressions` hygiene scan flagged `// eslint-disable-next-line react-hooks/exhaustive-deps` guarding the mount-only localStorage-restore effect in `JournalsViewContent` (`panel/src/components/journals/journals-view.tsx`). That effect restores the `agent`/`type`/`task` filters saved from a prior visit into the URL, but only on a fresh `/agents?tab=journals` visit that carries no query params yet — it must run exactly once per mount, never again, or it would clobber a later intentional "clear filters" action with stale saved state.
### Audit result: fixed at the source, no waiver needed
The suppression was removed by replacing the empty `[]` dependency array with a `useRef` mount-guard:
```tsx
const hasRestoredRef = useRef(false);
useEffect(() => {
if (hasRestoredRef.current) return;
hasRestoredRef.current = true;
// ...restore-from-localStorage logic, reading urlAgentId/urlType/urlTask/searchParams/router
}, [urlAgentId, urlType, urlTask, searchParams, router]);
```
The ref guard, not the (now honest and complete) dependency array, is what enforces "exactly once per mount" — so `react-hooks/exhaustive-deps` is satisfied without changing the effect's actual behavior. This is the same idiom already used elsewhere in the panel for mount-only effects: `panel/src/components/scroll-restoration.tsx` (`hasRestored`) and `panel/src/components/a2a/a2a-transcript.tsx` (`hasScrolledRef`). Prefer this pattern over `eslint-disable` + `[]` for any new mount-only effect — it keeps the dependency array truthful for future maintainers while still only firing once.
A naive fix that just added `router`/`searchParams` to the deps array **without** the ref guard would have been wrong: those values change across every navigation, so the effect would re-run on every subsequent URL change and re-apply the stale saved filters over a user's later, intentional filter clear. A regression suite, `panel/src/components/journals/__tests__/journals-view.test.tsx`, locks in the correct behavior: the restore fires once on a fresh no-param visit, is skipped when the URL already carries params, and — the key regression guard — never re-fires after mount even once the URL round-trips through params and back to empty.
No entry was added to `.roboco/conventions.yml`'s `waivers:` list — the suppression was eliminated at the source, not exempted. (The backend cell's unrelated waiver entries already committed to that file were left untouched.)