fix(views): resolve useEffect anti-patterns across Views and Audit-Report components#2947
fix(views): resolve useEffect anti-patterns across Views and Audit-Report components#2947
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
|
@copilot fix the build error |
|
@copilot rebase with main and resolve conflicts |
Done in 5154ccd. Merged |
|
@copilot I'm still seeing conflicts |
Fixed in |
c8a44bf to
db6738f
Compare
|
@coderabbitai review one more time |
|
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/pages/audit-report/components/View/ViewTableFilterForm.tsx (1)
68-97:⚠️ Potential issue | 🟠 MajorHoist the empty
defaultFieldValuesfallback to a module-level constant.
defaultFieldValuesis included in theuseMemodependency array (line 89). Both call sites (View.tsx:297and the unit test) pass inline objects likedefaultFieldValues={{}}, which are new references on every parent render. This causes the memo to recalculate unnecessarily on each parent render. WithenableReinitialize=true, Formik may reset the form state even when values haven't changed. Move the empty object to a module-level constant to prevent this.🔧 Suggested fix
+const EMPTY_FIELD_VALUES: Record<string, string> = {}; + export default function ViewTableFilterForm({ children, filterFields, - defaultFieldValues = {}, + defaultFieldValues = EMPTY_FIELD_VALUES, tablePrefix }: ViewTableFilterFormProps) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/audit-report/components/View/ViewTableFilterForm.tsx` around lines 68 - 97, The memo is recalculating because callers pass inline empty objects for the defaultFieldValues prop; hoist the empty fallback to a module-level constant and use that constant as the default value for the defaultFieldValues param in ViewTableFilterForm so the reference is stable. Update the component signature to default defaultFieldValues to the new constant, keep useMemo([tableParams, filterFields, defaultFieldValues]) as-is, and ensure tests/call sites can still pass overrides; this prevents unnecessary recomputation and Formik reinitialization while preserving current logic in useMemo and enableReinitialize.src/pages/audit-report/components/View/GlobalFiltersForm.tsx (1)
58-93:⚠️ Potential issue | 🟠 MajorStabilize the params snapshot before feeding
enableReinitialize.
usePrefixedSearchParamscreates a newURLSearchParamsreference on every router search params change (viafilterPrefixedParams). BecauseinitialValuesdepends on that object directly, every form→URL write rebuilds the Formik input and causesenableReinitializeto recalculate unnecessarily. While Formik's deep-equality check prevents spurious resets when the URL reflects the current form values, this approach is fragile and inefficient. Use a stable value-based dependency (globalParams.toString()) instead of theURLSearchParamsreference.🔧 Proposed fix
const [globalParams] = usePrefixedSearchParams(globalVarPrefix); + const globalParamsKey = globalParams.toString(); const initialValues = useMemo<Record<string, string>>(() => { + const params = new URLSearchParams(globalParamsKey); const values: Record<string, string> = {}; variables.forEach((variable) => { - const urlValue = globalParams.get(variable.key); + const urlValue = params.get(variable.key); const currentValue = currentVariables?.[variable.key]; const optionItems = variable.optionItems ?? []; const defaultValue = @@ }); return values; - }, [globalParams, variables, currentVariables]); + }, [globalParamsKey, variables, currentVariables]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/audit-report/components/View/GlobalFiltersForm.tsx` around lines 58 - 93, The initialValues useMemo currently depends on the URLSearchParams reference returned by usePrefixedSearchParams (globalParams), which changes identity on every router update and forces unnecessary re-evaluation; change the dependency to a stable, value-based snapshot by using globalParams.toString() (or an equivalent stringified value) in the useMemo dependency array and use that snapshot when computing values so that initialValues only recomputes when the actual query string content changes, keeping enableReinitialize stable; update the dependency list from [globalParams, variables, currentVariables] to [globalParams.toString(), variables, currentVariables] and reference the stringified snapshot when needed.
♻️ Duplicate comments (1)
src/pages/views/ViewPage.tsx (1)
39-45:⚠️ Potential issue | 🟡 MinorScope
errorand the first-paint loading state tolookupKeytoo.
fetchedIdis route-scoped now, but the render still reads the previous route's rawerror/isLoading. That means/view/:namecan briefly hit the"No view identifier provided"branch before the effect flips loading, and a failed lookup can still flash its stale error during the next/view/:idnavigation. Please key those render guards to the current route as well.Possible direction
const scopedFetchedId = lookupKey && fetchedLookupKey === lookupKey ? fetchedId : undefined; + const routeError = + id || !error + ? null + : lookupKey + ? fetchedLookupKey === lookupKey + ? error + : null + : error; + const lookupPending = !id && !!lookupKey && fetchedLookupKey !== lookupKey; // Derived: prefer the direct `id` param; fall back to whatever we fetched. const viewId = id ?? scopedFetchedId;Then render from
isLoading || lookupPendingandrouteErrorbelow instead of the raw state.Please verify by navigating
/view/:id -> /view/:nameand/view/missing -> /view/:id; neither flow should flash the empty/error state anymore.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/views/ViewPage.tsx` around lines 39 - 45, Scope the raw lookup state to the current route like you did for fetchedId: derive scopedError and scopedIsLoading using the same check (e.g. scopedError = lookupKey && fetchedLookupKey === lookupKey ? error : undefined; scopedIsLoading = lookupKey && fetchedLookupKey === lookupKey ? isLoading : false), then compute routeError/routeIsLoading (or reuse those names) and use routeIsLoading || lookupPending and routeError in the render guards instead of the raw isLoading/error; keep viewId as id ?? scopedFetchedId so the identifier logic remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/pages/audit-report/components/View/GlobalFiltersForm.tsx`:
- Around line 58-93: The initialValues useMemo currently depends on the
URLSearchParams reference returned by usePrefixedSearchParams (globalParams),
which changes identity on every router update and forces unnecessary
re-evaluation; change the dependency to a stable, value-based snapshot by using
globalParams.toString() (or an equivalent stringified value) in the useMemo
dependency array and use that snapshot when computing values so that
initialValues only recomputes when the actual query string content changes,
keeping enableReinitialize stable; update the dependency list from
[globalParams, variables, currentVariables] to [globalParams.toString(),
variables, currentVariables] and reference the stringified snapshot when needed.
In `@src/pages/audit-report/components/View/ViewTableFilterForm.tsx`:
- Around line 68-97: The memo is recalculating because callers pass inline empty
objects for the defaultFieldValues prop; hoist the empty fallback to a
module-level constant and use that constant as the default value for the
defaultFieldValues param in ViewTableFilterForm so the reference is stable.
Update the component signature to default defaultFieldValues to the new
constant, keep useMemo([tableParams, filterFields, defaultFieldValues]) as-is,
and ensure tests/call sites can still pass overrides; this prevents unnecessary
recomputation and Formik reinitialization while preserving current logic in
useMemo and enableReinitialize.
---
Duplicate comments:
In `@src/pages/views/ViewPage.tsx`:
- Around line 39-45: Scope the raw lookup state to the current route like you
did for fetchedId: derive scopedError and scopedIsLoading using the same check
(e.g. scopedError = lookupKey && fetchedLookupKey === lookupKey ? error :
undefined; scopedIsLoading = lookupKey && fetchedLookupKey === lookupKey ?
isLoading : false), then compute routeError/routeIsLoading (or reuse those
names) and use routeIsLoading || lookupPending and routeError in the render
guards instead of the raw isLoading/error; keep viewId as id ?? scopedFetchedId
so the identifier logic remains unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8eab1178-4f32-4ff6-a000-f427381f43a0
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
src/api/services/views.tssrc/pages/audit-report/components/View/GlobalFiltersForm.tsxsrc/pages/audit-report/components/View/ViewTableFilterForm.tsxsrc/pages/audit-report/components/View/panels/TimeseriesPanel.tsxsrc/pages/views/ViewPage.tsxsrc/pages/views/ViewsPage.tsx
db6738f to
543a6f6
Compare
…tate in ViewPage Co-authored-by: adityathebe <13419481+adityathebe@users.noreply.github.com>
…ffect in ViewsPage Co-authored-by: adityathebe <13419481+adityathebe@users.noreply.github.com>
…Ticks with useMemo in TimeseriesPanel Co-authored-by: adityathebe <13419481+adityathebe@users.noreply.github.com>
…and ViewTableFilterForm; use initialValues instead Co-authored-by: adityathebe <13419481+adityathebe@users.noreply.github.com>
…ude URL params in initialValues deps Co-authored-by: adityathebe <13419481+adityathebe@users.noreply.github.com>
…d by mistake Co-authored-by: adityathebe <13419481+adityathebe@users.noreply.github.com>
…e to resolve PR conflict Co-authored-by: adityathebe <13419481+adityathebe@users.noreply.github.com>
543a6f6 to
5fb28d2
Compare
idparam is presentfilterFieldsto ViewTableFilterForm useEffect dependency array (remove eslint-disable)variablesto GlobalFiltersForm useEffect dep array – maintainer confirmed this cannot be done (would cause infinite re-renders if caller passes a new array ref each render)Original prompt
💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.
Summary by CodeRabbit