Skip to content

fix(AnalyticalTable): prevent empty table body flash on first render#8375

Merged
Lukas742 merged 4 commits intoUI5:mainfrom
abzokhattab:fix/analytical-table-body-flash
Mar 25, 2026
Merged

fix(AnalyticalTable): prevent empty table body flash on first render#8375
Lukas742 merged 4 commits intoUI5:mainfrom
abzokhattab:fix/analytical-table-body-flash

Conversation

@abzokhattab
Copy link
Contributor

@abzokhattab abzokhattab commented Mar 23, 2026

Description

When an AnalyticalTable with visibleRowCountMode="Auto" mounts, there is a brief but visible flash where the table headers and toolbar are fully rendered while the body area is empty. After a frame the rows appear, but the flicker is noticeable — especially in applications where the table is rendered inside a layout that itself triggers re-layouts on mount (e.g. an ObjectPage with IconTabBar mode).

Root cause

VirtualTableBodyContainer needs the body <div> to be in the DOM before the virtualizer can measure it and render rows. To enforce this, it gates the row children behind an isMounted flag:

const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
  if (parentRef.current) {
    setIsMounted(true);
  }
}, [parentRef]);

// later in JSX:
{isMounted && children}

The problem is the choice of useEffect. React's useEffect runs after the browser paints, which means every mount follows this sequence:

  1. React commits the component tree to the DOM — the body <div ref={parentRef}> is in the document, but isMounted is still false, so children (the rows) are not part of the tree.
  2. The browser paints this state — the user sees a complete table frame with an empty body.
  3. Only now does useEffect fire, finding parentRef.current set and calling setIsMounted(true).
  4. React re-renders with the rows included, the browser paints again — the table is now complete.

Step 2 is the flash. It is a single frame, but it is consistently reproducible on every mount.

Fix

Replace useEffect with useLayoutEffect. Because useLayoutEffect runs synchronously after DOM mutations but before the browser paints, the ref check and state update happen in time for React to include the rows in the very first paint. The user never sees an empty body.

Why this is safe

  • parentRef.current is guaranteed to be available when the layout effect runs — the <div ref={parentRef}> renders unconditionally in the same component's JSX, so the ref is assigned during the commit phase before any effects fire.
  • The AnalyticalTable itself already uses useIsomorphicLayoutEffect (which resolves to useLayoutEffect on the client) in its main index.tsx for similar DOM-measurement work, so this is consistent with the existing codebase.
  • This is exactly the use case described in the React docs on useEffect: "If your Effect is doing something visual (for example, positioning a tooltip), and the delay is noticeable (for example, it flickers), replace useEffect with useLayoutEffect."

Result

Before — empty body flashes for one frame on every mount:

Screen.Recording.2026-03-23.at.23.01.01.mov

After — rows appear together with headers, no flash:

Screen.Recording.2026-03-23.at.22.56.02.mov

Use useLayoutEffect instead of useEffect for the isMounted state
in VirtualTableBodyContainer to eliminate a one-frame flash where
table headers render without any body rows.
@cla-assistant
Copy link

cla-assistant bot commented Mar 23, 2026

CLA assistant check
All committers have signed the CLA.

@abzokhattab abzokhattab marked this pull request as ready for review March 23, 2026 22:16
Copy link
Contributor

@Lukas742 Lukas742 left a comment

Choose a reason for hiding this comment

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

Hi @abzokhattab

Thanks for the contribution! The PR looks good to me, I only have a small remark regarding consistency. (See below)

@coveralls
Copy link

Pull Request Test Coverage Report for Build 23461917284

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 1 of 1 (100.0%) changed or added relevant line in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 84.809%

Totals Coverage Status
Change from base Build 23432255870: 0.0%
Covered Lines: 6521
Relevant Lines: 7329

💛 - Coveralls

abzokhattab and others added 2 commits March 24, 2026 23:15
Use useIsomorphicLayoutEffect instead of useEffect when toggling isMounted in VirtualTableBodyContainer so rows render before the first paint.
@abzokhattab abzokhattab requested a review from Lukas742 March 25, 2026 09:37
Copy link
Contributor

@Lukas742 Lukas742 left a comment

Choose a reason for hiding this comment

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

🚢

@Lukas742 Lukas742 added this pull request to the merge queue Mar 25, 2026
Merged via the queue into UI5:main with commit a4fb335 Mar 25, 2026
27 checks passed
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.

3 participants