Optimize Filter UI (reduce rerendering)#3633
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughExtracted inline filter UI into local helper components across many table pages and removed the Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
apps/web/ui/referrals/partner-referral-table.tsx (1)
291-291: Use a stable empty object reference to prevent unnecessary memoization recomputes.The inline
{}on line 291 creates a new object reference on every render. SinceextraSearchParamsis included in the dependency array ofuseMemoon line 154 ofuse-program-referral-filters.tsx, this causes thesearchQuerymemo to recompute unnecessarily each render.♻️ Suggested fix
+const EMPTY_FILTERS_OPTIONS = {}; + function PartnerReferralFilters() { const { filters, activeFilters, onSelect, onRemove, onRemoveAll, setSearch, setSelectedFilter, - } = useProgramReferralsFilters({}); + } = useProgramReferralsFilters(EMPTY_FILTERS_OPTIONS);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/ui/referrals/partner-referral-table.tsx` at line 291, The inline {} passed into useProgramReferralsFilters creates a new object each render causing extraSearchParams to change and trigger recomputations of the searchQuery useMemo in use-program-referral-filters.tsx; fix by passing a stable reference instead (e.g., export and use a shared constant like EMPTY_EXTRA_SEARCH_PARAMS or useRef to hold an immutable empty object) when calling useProgramReferralsFilters so extraSearchParams remains stable and prevents unnecessary memo recomputes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/commissions/commissions-table.tsx:
- Around line 383-386: The empty-state message is wrong because the check uses
Object.keys(searchParamsObj).length > 0 and treats sorting params as filters;
update the logic (inside the commissions-table component where searchParamsObj
is used) to ignore sorting keys (e.g., "sortBy" and "sortOrder") when deciding
if filters are active—compute something like filteredKeys =
Object.keys(searchParamsObj).filter(k => !["sortBy","sortOrder"].includes(k))
and use filteredKeys.length > 0 to choose between "No commissions found for the
selected filters." and "No commissions have been made for this program yet."
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/fraud/resolved/resolved-fraud-group-table.tsx:
- Line 33: isFiltered currently treats any URL param as a filter, so when
onSortChange writes sortBy/sortOrder the UI incorrectly shows the filtered empty
state; update the isFiltered computation to ignore sorting params by deriving a
new object or checking keys excluding "sortBy" and "sortOrder" (e.g. compute
filteredSearchParams = Object.keys(searchParamsObj).filter(k => k !== "sortBy"
&& k !== "sortOrder") and use its length) so that isFiltered only reflects true
filters; locate usage around the isFiltered declaration and where onSortChange
sets sortBy/sortOrder to ensure consistency.
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/groups/groups-table.tsx:
- Line 91: isFiltered currently uses Object.keys(searchParamsObj).length > 0
which treats sorting/pagination as filters; change the logic in the groups-table
component to ignore sort and pagination params when computing isFiltered by
checking only actual filter keys (e.g., compute filteredKeys =
Object.keys(searchParamsObj).filter(k =>
!["sortBy","sortOrder","page","limit"].includes(k)) and set isFiltered =
filteredKeys.length > 0) or simply use the existing search param check
(!!searchParams.get("search")) if only search is a real filter; update the
isFiltered calculation where searchParamsObj and isFiltered are defined to
reference these symbols.
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx:
- Around line 569-572: The message currently treats any searchParamsObj entries
(including sorting params set by onSortChange) as filters; update the isFiltered
check to ignore sorting keys so sorting alone doesn't count as filtered. Locate
the code that computes isFiltered / the conditional using searchParamsObj (and
see onSortChange for which keys it sets) and replace
Object.keys(searchParamsObj).length > 0 with a filtered-key check that excludes
the sort-related param names used by onSortChange (e.g., 'sort', 'order',
'direction' or the exact keys onSortChange writes) and then checks length > 0 on
that filtered array so only true filters trigger the "filtered" message.
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/payouts/payout-table.tsx:
- Around line 211-213: The current conditional uses searchParamsObj presence to
decide the empty-state message, which treats any query param (including
pagination/sort) as a filter; change it to detect only actual filter keys by
defining a whitelist (e.g., "status", "startDate", "endDate", "beneficiaryId",
etc.) or a blacklist for non-filter keys ("page", "limit", "sort") and replace
the Object.keys(searchParamsObj).length > 0 check with a utility like
isFilterApplied(searchParamsObj) that returns true only when at least one filter
key is present; update the ternary that renders the message (the expression
using searchParamsObj) to call that utility instead so sorting/pagination params
no longer trigger the "filtered" message.
In `@apps/web/ui/customers/customers-table/customers-table.tsx`:
- Line 76: isFiltered currently treats any URL param (including sortBy/sortOrder
set by onSortChange) as a filter; change the check so it ignores sorting params.
Update the computation around isFiltered in customers-table.tsx to derive it
from Object.keys(searchParamsObj).filter(k => k !== 'sortBy' && k !==
'sortOrder').length > 0 (or equivalently remove those keys before counting) so
clicking a column header doesn't trigger the "filtered" empty-state; reference
the existing searchParamsObj, isFiltered and the onSortChange behavior that
writes sortBy/sortOrder.
---
Nitpick comments:
In `@apps/web/ui/referrals/partner-referral-table.tsx`:
- Line 291: The inline {} passed into useProgramReferralsFilters creates a new
object each render causing extraSearchParams to change and trigger
recomputations of the searchQuery useMemo in use-program-referral-filters.tsx;
fix by passing a stable reference instead (e.g., export and use a shared
constant like EMPTY_EXTRA_SEARCH_PARAMS or useRef to hold an immutable empty
object) when calling useProgramReferralsFilters so extraSearchParams remains
stable and prevents unnecessary memo recomputes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 051e3930-19e4-4875-96e9-043f16b32a6d
📒 Files selected for processing (16)
apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/payout-table.tsxapps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/use-payout-filters.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/bounties/[bountyId]/bounty-submissions-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/campaigns-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commissions-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/use-commission-filters.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/fraud-group-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolved/resolved-fraud-group-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/use-partner-filters.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/use-payout-filters.tsxapps/web/ui/customers/customers-table/customers-table.tsxapps/web/ui/customers/customers-table/use-customer-filters.tsxapps/web/ui/referrals/partner-referral-table.tsx
💤 Files with no reviewable changes (5)
- apps/web/app/(ee)/partners.dub.co/(dashboard)/payouts/use-payout-filters.tsx
- apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/use-commission-filters.tsx
- apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/use-partner-filters.tsx
- apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/use-payout-filters.tsx
- apps/web/ui/customers/customers-table/use-customer-filters.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx:
- Line 490: The empty-state message currently treats any query params as
"selected filters" by using Object.keys(searchParamsObj).length > 0; change this
to detect only actual filter/search signals — e.g., check the explicit filter
keys you use (such as status, stage, applicationType, query/search term,
dateRange) or add a helper like isAnyFilterApplied(searchParamsObj, search) that
returns true only if known filter keys are present or search is non-empty; then
replace the condition in the description template (the expression using
searchParamsObj and search) to call that helper or test those specific keys so
the copy correctly shows "for the selected filters" only when real
filters/search are active.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8eda3d41-027c-436a-9fd8-a290aca15fd1
📒 Files selected for processing (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/rejected/page-client.tsx (1)
123-125:⚠️ Potential issue | 🟡 MinorExclude
partnerIdfromisFilteredhere too.This page uses
partnerIdfor the details sheet, so counting every non-sort query param as a filter can still produce the wrong empty-state copy when no real filter/search is active.Suggested fix
const isFiltered = Object.keys(searchParamsObj).some( - (key) => !["sortBy", "sortOrder", "page"].includes(key), + (key) => !["sortBy", "sortOrder", "page", "partnerId"].includes(key), );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/partners/applications/rejected/page-client.tsx around lines 123 - 125, The isFiltered calculation currently treats any query param other than "sortBy", "sortOrder", and "page" as a filter; update the logic that computes isFiltered (the Object.keys(searchParamsObj).some(...) call) to also exclude "partnerId" so that partnerId does not count as an active filter—i.e., add "partnerId" to the list of keys ignored when determining whether a real filter/search is present.apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx (1)
71-73:⚠️ Potential issue | 🟡 MinorExclude
partnerIdfromisFiltered.
partnerIddrives the application sheet, not filtering. If it lingers in the URL, the empty state flips to “selected filters” even when no actual search/filter is active.Suggested fix
const isFiltered = Object.keys(searchParamsObj).some( - (key) => !["sortBy", "sortOrder", "page"].includes(key), + (key) => !["sortBy", "sortOrder", "page", "partnerId"].includes(key), );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx around lines 71 - 73, The isFiltered check is incorrectly treating partnerId as a filter; update the predicate that computes isFiltered (where searchParamsObj is iterated and key is tested) to also ignore "partnerId" — i.e., add "partnerId" to the list of keys excluded from filtering (the same place that currently excludes "sortBy", "sortOrder", and "page" in the isFiltered computation).
🧹 Nitpick comments (1)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx (1)
589-633:PartnersFiltersstill rerenders with everyPartnersTablecommit.Pulling the filter UI into a plain child component is mostly an organization change; selection/pagination updates in
PartnersTablewill still rerender this component and rerunusePartnerFilters. If this PR is meant to reduce filter rerenders, please confirm with the React Profiler and wrap this helper inmemo.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx around lines 589 - 633, PartnersFilters is still rerendering on every PartnersTable commit because it's a plain function component that re-runs usePartnerFilters; wrap it with React.memo so React can skip rerenders when its props (sortBy, sortOrder, status) are unchanged and ensure you import memo from 'react' (or export default memo(PartnersFilters)); reference the component name PartnersFilters and the custom hook usePartnerFilters when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/campaigns/campaigns-table.tsx:
- Around line 47-50: The current isFiltered calculation treats any
non-pagination/sort query key as an active filter; update the logic in the
campaigns-table component (where isFiltered is computed using useRouterStuff and
searchParamsObj) to only consider known filter keys and only count them when
their values are non-empty (trimmed string or non-empty array/object).
Concretely, replace the broad Object.keys check with a whitelist of filter keys
(e.g., name, status, owner, dateFrom, dateTo, etc.) and verify each
searchParamsObj[key] has a meaningful value before returning true so unrelated
or empty query params no longer trigger the filtered state.
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/fraud/resolved/resolved-fraud-group-table.tsx:
- Around line 33-35: isFiltered currently treats any searchParamsObj key other
than "sortBy", "sortOrder", and "page" as a filter; exclude the details-sheet
URL state "groupId" from that detection so a URL with only groupId doesn't mark
the table as filtered. Update the isFiltered check (referencing the isFiltered
constant and searchParamsObj) to also ignore "groupId" (e.g., add "groupId" to
the array of non-filter keys or explicitly filter it out before the .some check)
so that only actual filters affect the empty-state description.
---
Duplicate comments:
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx:
- Around line 71-73: The isFiltered check is incorrectly treating partnerId as a
filter; update the predicate that computes isFiltered (where searchParamsObj is
iterated and key is tested) to also ignore "partnerId" — i.e., add "partnerId"
to the list of keys excluded from filtering (the same place that currently
excludes "sortBy", "sortOrder", and "page" in the isFiltered computation).
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/partners/applications/rejected/page-client.tsx:
- Around line 123-125: The isFiltered calculation currently treats any query
param other than "sortBy", "sortOrder", and "page" as a filter; update the logic
that computes isFiltered (the Object.keys(searchParamsObj).some(...) call) to
also exclude "partnerId" so that partnerId does not count as an active
filter—i.e., add "partnerId" to the list of keys ignored when determining
whether a real filter/search is present.
---
Nitpick comments:
In
`@apps/web/app/app.dub.co/`(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx:
- Around line 589-633: PartnersFilters is still rerendering on every
PartnersTable commit because it's a plain function component that re-runs
usePartnerFilters; wrap it with React.memo so React can skip rerenders when its
props (sortBy, sortOrder, status) are unchanged and ensure you import memo from
'react' (or export default memo(PartnersFilters)); reference the component name
PartnersFilters and the custom hook usePartnerFilters when making the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 03fd3d8f-970f-4ba0-b5cd-4fc96d5b9ab0
📒 Files selected for processing (9)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/campaigns/campaigns-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commissions-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/fraud/resolved/resolved-fraud-group-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/rejected/page-client.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsxapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-table.tsxapps/web/ui/customers/customers-table/customers-table.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/groups/groups-table.tsx
- apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/commissions/commissions-table.tsx
- apps/web/ui/customers/customers-table/customers-table.tsx
- apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/payout-table.tsx
Summary by CodeRabbit