Skip to content

Fix users table rerendering#987

Closed
BilalG1 wants to merge 8 commits intodevfrom
fix-users-table-rerendering
Closed

Fix users table rerendering#987
BilalG1 wants to merge 8 commits intodevfrom
fix-users-table-rerendering

Conversation

@BilalG1
Copy link
Contributor

@BilalG1 BilalG1 commented Oct 29, 2025

https://www.loom.com/share/d48bd3dd587840ac9104765f8c83efff

Summary by CodeRabbit

  • New Features

    • Loading skeletons for table rows and per-column placeholders.
    • Toggle to include/exclude anonymous users in user lists.
    • External refresh key support to reset pagination when relevant state changes.
  • Improvements

    • Debounced/smoother search and global filter behavior.
    • Safer cancellable data fetches to prevent stale updates.
    • Unified richer user data surface and minor UI spacing/text refinements.

@vercel
Copy link

vercel bot commented Oct 29, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Oct 31, 2025 11:40am
stack-dashboard Ready Ready Preview Comment Oct 31, 2025 11:40am
stack-demo Ready Ready Preview Comment Oct 31, 2025 11:40am
stack-docs Ready Ready Preview Comment Oct 31, 2025 11:40am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a loadingState API with skeleton-row rendering, a microtask-debounced toolbar search, cancellable fetches and includeAnonymous toggle in the dashboard user table, plus externalRefreshKey propagation and a cache-key change to consider includeAnonymous.

Changes

Cohort / File(s) Summary
Data-table core (UI layer)
packages/stack-ui/src/components/data-table/data-table.tsx
Added loadingState?: { isLoading: boolean, rowCount?: number } to TableView/DataTable/DataTableManualPagination/DataTableBase; render skeleton rows when loading; compute visible columns for loading cells; add GLOBAL_FILTER_DEBOUNCE_MS = 300; add externalRefreshKey to manual pagination and reset behaviour.
Toolbar search
packages/stack-ui/src/components/data-table/toolbar-items.tsx
Switch SearchToolbarItem to local search state and an onChange that updates local state then schedules a microtask-debounced application of column/global filters to avoid immediate re-renders.
Dashboard user table
apps/dashboard/src/components/data-table/user-table.tsx
Introduced includeAnonymous state and handleIncludeAnonymousChange, added skeletons to Avatar/Actions columns, replaced local filters with ExtendedServerUser surface, implemented cancellable guarded fetch flow using a requestId, computed externalRefreshKey, and passed loadingState/externalRefreshKey to DataTableManualPagination. Added TableType alias usage.
Server app cache key
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts
Extended useUsers() dependency/cache key to include options?.includeAnonymous, making include-anonymous part of cache lookup/invalidation.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant U as User
    participant UT as UserTable
    participant DT as DataTable
    participant API as serverApp.listUsers

    U->>UT: toggle includeAnonymous / change search/page
    activate UT
    UT->>UT: update includeAnonymous, filters, bump requestId
    UT->>DT: set loadingState.isLoading = true and externalRefreshKey
    deactivate UT

    activate DT
    DT->>DT: render skeleton rows based on visibleColumns
    deactivate DT

    UT->>API: call listUsers with current params
    activate API
    API-->>UT: returns users (response with requestId)
    deactivate API

    UT->>UT: compare response.requestId vs latestRequestId
    alt requestId matches latest
        UT->>UT: update users state
        UT->>DT: set loadingState.isLoading = false
    else stale
        UT->>UT: discard result
    end

    activate DT
    DT->>DT: render data rows
    deactivate DT
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review requestId guard and isFetching semantics in apps/dashboard/src/components/data-table/user-table.tsx
  • Verify loadingState propagation and skeleton cell alignment with visibleColumns in packages/stack-ui/src/components/data-table/data-table.tsx
  • Check debounce interactions between GLOBAL_FILTER_DEBOUNCE_MS and microtask-based toolbar scheduling
  • Validate cache-key change in server-app-impl.ts for intended cache behavior

Possibly related PRs

  • Improved anonymous users #857 — similar changes to dashboard user table and include-anonymous toggle/data flow; likely overlaps with toolbar and fetch logic.

Suggested reviewers

  • N2D4

Poem

🐇 I hopped through rows with ears held high,

skeletons shimmered while queries fly,
I guard each request with a clever id,
toggle anon — the list stays tidied,
a tiny rabbit claps — code verified.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description is largely incomplete, containing only a Loom video link and the template's HTML comment about reading CONTRIBUTING.md. There is no written explanation of what problem the changes solve, why the modifications were made, what the implementation does, or any other contextual information. While the template provided is minimal and doesn't specify detailed required sections, a functional PR description should include at least basic textual information about the changes. Relying solely on a video link without accompanying written explanation is insufficient for a proper code review.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "Fix users table rerendering" is related to the changeset and mentions the specific component affected (users table). However, the title is vague about what specifically was fixed. The actual changes span multiple aspects including race condition prevention via requestId guards, implementation of external refresh keys, addition of loading state indicators with skeleton rendering, cache key modifications, and debounce optimizations. A teammate scanning the commit history would not have sufficient clarity about whether the issue involved performance, race conditions, loading states, or caching behavior. The title is too generic to clearly summarize the primary change given the scope and complexity of the modifications.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-users-table-rerendering

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 349248c and ff0c4df.

📒 Files selected for processing (1)
  • apps/dashboard/src/components/data-table/user-table.tsx (8 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use ES6 Maps instead of Records wherever possible in TypeScript code

Files:

  • apps/dashboard/src/components/data-table/user-table.tsx
apps/{dashboard,dev-launchpad}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

For blocking alerts and errors in the UI, never use toast; use alerts instead

Files:

  • apps/dashboard/src/components/data-table/user-table.tsx
apps/{dashboard,dev-launchpad}/**/*.{css,tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Animations: keep hover/click transitions snappy; do not delay actions with pre-hover transitions; apply transitions after the action (e.g., fade-out on hover end)

Files:

  • apps/dashboard/src/components/data-table/user-table.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-20T22:25:40.427Z
Learnt from: CR
PR: stack-auth/stack-auth#0
File: AGENTS.md:0-0
Timestamp: 2025-10-20T22:25:40.427Z
Learning: Applies to packages/stack-ui/**/*.{css,tsx,jsx} : Animations in shared UI: keep interactions snappy; avoid pre-action transitions; apply transitions after the action

Applied to files:

  • apps/dashboard/src/components/data-table/user-table.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/components/data-table/user-table.tsx (3)
packages/stack-ui/src/components/data-table/cells.tsx (1)
  • TextCell (7-43)
packages/stack-ui/src/components/simple-tooltip.tsx (1)
  • SimpleTooltip (5-46)
packages/stack-ui/src/components/data-table/data-table.tsx (1)
  • DataTableManualPagination (212-293)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: build (22.x)
  • GitHub Check: setup-tests
  • GitHub Check: restart-dev-and-test
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: all-good
  • GitHub Check: Vercel Agent Review
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: docker
  • GitHub Check: Security Check

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Refactored user table to fix rerendering issues by implementing async state management, loading skeletons, and request deduplication.

  • Converted from synchronous hook-based data fetching to async state-managed approach with isFetching flag and loading states
  • Added loading skeleton rows with custom loading cells for avatar and actions columns
  • Implemented request ID tracking to prevent race conditions and stale data updates
  • Added externalRefreshKey prop to trigger table refresh when includeAnonymous filter changes
  • Decoupled search input state from table state to prevent input lag using local state + deferred table updates

Confidence Score: 2/5

  • This PR has a critical bug that will cause runtime errors when the table has no sorting applied
  • Score reflects the critical sorting bug in user-table.tsx:223 where primarySort can be undefined, causing primarySort.id and primarySort.desc to throw errors. Also includes unused imports and ineffective setTimeout pattern
  • apps/dashboard/src/components/data-table/user-table.tsx requires immediate attention to fix sorting logic bug

Important Files Changed

File Analysis

Filename Score Overview
apps/dashboard/src/components/data-table/user-table.tsx 2/5 Refactored to use async state management with loading states and request deduplication. Critical bug: sorting logic will fail when sorting array is empty
packages/stack-ui/src/components/data-table/data-table.tsx 4/5 Added loading skeleton support and externalRefreshKey prop for controlled refreshes. Changes look solid
packages/stack-ui/src/components/data-table/toolbar-items.tsx 3/5 Added local state to search input with setTimeout workaround. Ineffective setTimeout pattern could be improved

Sequence Diagram

sequenceDiagram
    participant User
    participant SearchInput
    participant UserTable
    participant DataTableManualPagination
    participant AdminApp
    
    User->>SearchInput: Types search query
    SearchInput->>SearchInput: Updates local state immediately
    SearchInput->>DataTableManualPagination: setGlobalFilter (after setTimeout 0ms)
    DataTableManualPagination->>DataTableManualPagination: Debounces for 300ms
    DataTableManualPagination->>UserTable: Triggers onUpdate callback
    
    UserTable->>UserTable: Increment latestRequestIdRef
    UserTable->>UserTable: setIsFetching(true)
    UserTable->>AdminApp: listUsers(filters)
    AdminApp-->>UserTable: Returns users data
    
    alt Request is latest
        UserTable->>UserTable: setUsers(freshUsers)
        UserTable->>UserTable: setIsFetching(false)
    else Request is stale
        UserTable->>UserTable: Ignore stale response
        UserTable->>UserTable: setIsFetching(false)
    end
    
    UserTable->>DataTableManualPagination: Display loading skeletons
    DataTableManualPagination->>User: Show updated table
Loading

3 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a8e3ff and 61c68da.

📒 Files selected for processing (3)
  • apps/dashboard/src/components/data-table/user-table.tsx (8 hunks)
  • packages/stack-ui/src/components/data-table/data-table.tsx (15 hunks)
  • packages/stack-ui/src/components/data-table/toolbar-items.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use ES6 Maps instead of Records wherever possible in TypeScript code

Files:

  • packages/stack-ui/src/components/data-table/toolbar-items.tsx
  • apps/dashboard/src/components/data-table/user-table.tsx
  • packages/stack-ui/src/components/data-table/data-table.tsx
packages/{stack-ui,react}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

For blocking alerts and errors in shared/UI packages, never use toast; use alerts instead

Files:

  • packages/stack-ui/src/components/data-table/toolbar-items.tsx
  • packages/stack-ui/src/components/data-table/data-table.tsx
packages/stack-ui/**/*.{css,tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Animations in shared UI: keep interactions snappy; avoid pre-action transitions; apply transitions after the action

Files:

  • packages/stack-ui/src/components/data-table/toolbar-items.tsx
  • packages/stack-ui/src/components/data-table/data-table.tsx
apps/{dashboard,dev-launchpad}/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

For blocking alerts and errors in the UI, never use toast; use alerts instead

Files:

  • apps/dashboard/src/components/data-table/user-table.tsx
apps/{dashboard,dev-launchpad}/**/*.{css,tsx,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Animations: keep hover/click transitions snappy; do not delay actions with pre-hover transitions; apply transitions after the action (e.g., fade-out on hover end)

Files:

  • apps/dashboard/src/components/data-table/user-table.tsx
🧠 Learnings (2)
📚 Learning: 2025-10-20T22:25:40.427Z
Learnt from: CR
PR: stack-auth/stack-auth#0
File: AGENTS.md:0-0
Timestamp: 2025-10-20T22:25:40.427Z
Learning: Applies to packages/stack-ui/**/*.{css,tsx,jsx} : Animations in shared UI: keep interactions snappy; avoid pre-action transitions; apply transitions after the action

Applied to files:

  • apps/dashboard/src/components/data-table/user-table.tsx
📚 Learning: 2025-10-11T04:13:19.308Z
Learnt from: N2D4
PR: stack-auth/stack-auth#943
File: examples/convex/app/action/page.tsx:23-28
Timestamp: 2025-10-11T04:13:19.308Z
Learning: In the stack-auth codebase, use `runAsynchronouslyWithAlert` from `stackframe/stack-shared/dist/utils/promises` for async button click handlers and form submissions instead of manual try/catch blocks. This utility automatically handles errors and shows alerts to users.

Applied to files:

  • packages/stack-ui/src/components/data-table/data-table.tsx
🧬 Code graph analysis (2)
packages/stack-ui/src/components/data-table/toolbar-items.tsx (1)
packages/stack-ui/src/components/ui/input.tsx (1)
  • Input (10-41)
apps/dashboard/src/components/data-table/user-table.tsx (4)
packages/stack-ui/src/components/data-table/cells.tsx (1)
  • TextCell (7-43)
packages/stack-ui/src/components/simple-tooltip.tsx (1)
  • SimpleTooltip (5-46)
packages/stack-shared/src/utils/paginated-lists.tsx (1)
  • empty (247-259)
packages/stack-ui/src/components/data-table/data-table.tsx (1)
  • DataTableManualPagination (212-293)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: Vercel Agent Review
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: build (22.x)
  • GitHub Check: all-good
  • GitHub Check: setup-tests
  • GitHub Check: docker
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: build (22.x)
  • GitHub Check: Security Check

@BilalG1 BilalG1 requested a review from N2D4 October 29, 2025 20:05
@BilalG1 BilalG1 assigned N2D4 and BilalG1 and unassigned BilalG1 and N2D4 Oct 29, 2025
@BilalG1 BilalG1 removed the request for review from N2D4 October 30, 2025 01:38
@BilalG1 BilalG1 marked this pull request as draft October 30, 2025 01:39
@BilalG1 BilalG1 marked this pull request as ready for review October 30, 2025 16:17
@BilalG1 BilalG1 requested a review from N2D4 October 30, 2025 16:17
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Oct 30, 2025
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Overview

Greptile Summary

Refactored user table to fix rerendering issues by moving from reactive state management to manual pagination with async data fetching. Added loading skeletons, debounced search, and external refresh key to reset pagination when includeAnonymous state changes.

Key changes:

  • Replaced reactive useUsers hook with manual state management using useState and async listUsers calls
  • Added request ID tracking to prevent race conditions from concurrent fetches
  • Implemented loading states with skeleton placeholders for better UX
  • Fixed cache invalidation by adding includeAnonymous parameter to cache key in useUsers hook
  • Added externalRefreshKey to trigger pagination reset when underlying data changes

Confidence Score: 2/5

  • Critical bug in sorting logic will cause runtime error if sorting state is ever cleared
  • The PR contains a previously identified logic bug where primarySort.id is accessed without null checking (line 240), which will throw a runtime error if the sorting array is empty. While defaultSorting is provided, users can potentially clear sorting through UI interactions, making this a real risk.
  • Pay close attention to apps/dashboard/src/components/data-table/user-table.tsx - the sorting logic bug on line 240 must be fixed before merging

Important Files Changed

File Analysis

Filename Score Overview
apps/dashboard/src/components/data-table/user-table.tsx 2/5 Refactored user table to use manual state management and added loading states. Contains critical sorting logic bug and inefficient externalRefreshKey implementation.
packages/template/src/lib/stack-app/apps/implementations/server-app-impl.ts 5/5 Added includeAnonymous parameter to cache key for useUsers hook - correct fix for cache invalidation.

Sequence Diagram

sequenceDiagram
    participant User
    participant UserTable
    participant DataTable
    participant StackAdminApp
    participant Cache

    User->>UserTable: Toggle "Show anonymous users"
    UserTable->>UserTable: setIncludeAnonymous(value)
    UserTable->>DataTable: table.setPageIndex(0)
    
    Note over UserTable: liveUsersPreview hook triggers
    UserTable->>StackAdminApp: useUsers({includeAnonymous})
    StackAdminApp->>Cache: useAsyncCache with includeAnonymous key
    Cache-->>StackAdminApp: Updated users data
    StackAdminApp-->>UserTable: liveUsersPreview updated
    
    Note over UserTable: externalRefreshKey recalculates
    UserTable->>UserTable: useMemo computes new key
    UserTable->>DataTable: externalRefreshKey change detected
    DataTable->>DataTable: Reset pagination & cursors
    DataTable->>UserTable: onUpdate callback
    
    UserTable->>UserTable: Generate nextFilters
    UserTable->>UserTable: requestId++, setIsFetching(true)
    UserTable->>StackAdminApp: listUsers(nextFilters)
    StackAdminApp->>Cache: getOrWait with includeAnonymous
    Cache-->>StackAdminApp: Fresh users data
    StackAdminApp-->>UserTable: Users with nextCursor
    UserTable->>UserTable: setUsers(freshUsers)
    UserTable->>UserTable: setIsFetching(false)
    UserTable->>DataTable: Return {nextCursor}
Loading

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@BilalG1 BilalG1 closed this Nov 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants