Skip to content

fix(ebay-tabs): prevent onSelect from firing on initial render#579

Open
dididy wants to merge 1 commit intoeBay:mainfrom
dididy:fix/407
Open

fix(ebay-tabs): prevent onSelect from firing on initial render#579
dididy wants to merge 1 commit intoeBay:mainfrom
dididy:fix/407

Conversation

@dididy
Copy link
Copy Markdown

@dididy dididy commented Mar 25, 2026

Description

onSelect fired on mount because useEffect(() => { handleSelect(index) }, [index]) runs on the initial render. Broke any analytics/tracking code that expected onSelect to mean "user clicked a tab."

Switched to tracking the previous index with a useRef. onSelect only fires when index actually changes. This is cleaner than the isMounted pattern -isMounted just suppresses the first call, while prevIndex expresses what we actually want: skip if nothing changed.

Notes

isMounted would've also worked, but it gets the edge case wrong: if the component mounts with a non-zero index and then receives the same value again, isMounted would fire onSelect anyway. prevIndex doesn't.

Screenshots

http://localhost:9001/?path=/story/navigation-disclosure-ebay-tabs--default-tabs

As-Is

스크린샷 2026-03-25 오후 7 36 16

To-Be

스크린샷 2026-03-25 오후 7 36 27

Checklist

  • I verify all changes are within scope of the linked issue
  • I added/updated/removed testing (Storybook in Skin) coverage as appropriate
  • I tested the UI in all supported browsers
  • I tested the UI in dark mode and RTL mode

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 25, 2026

🦋 Changeset detected

Latest commit: 516c8ac

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@ebay/ui-core-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Copy Markdown
Member

@HenriqueLimas HenriqueLimas left a comment

Choose a reason for hiding this comment

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

Thank you for the PR! I think the best approach would be to get rid of useEffect instead. We also need a unit test for use case

Comment on lines 65 to 70
useEffect(() => {
handleSelect(index);
if (prevIndex.current !== index) {
prevIndex.current = index;
handleSelect(index);
}
}, [index]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should remove the useEffect instead. We already call handleSelect on click

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes @ebay/ui-core-react’s EbayTabs so onSelect no longer fires on the initial mount, addressing issue #407 where mount-time callbacks broke analytics/tracking expectations.

Changes:

  • Track the previous selectedIndex via useRef and only invoke selection handling when the prop value changes.
  • Add a changeset to release the fix as a patch.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/ebayui-core-react/src/ebay-tabs/tabs.tsx Adds prevIndex tracking to avoid calling selection logic on initial render.
.changeset/cyan-toes-happen.md Declares a patch release note for the tabs onSelect behavior change.

handleSelect(index);
if (prevIndex.current !== index) {
prevIndex.current = index;
handleSelect(index);
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

The effect still calls handleSelect(index), which triggers onSelect. This means onSelect will fire for any prop-driven selectedIndex change after mount (not just user interaction), and it will also double-fire in a controlled pattern where the parent updates selectedIndex in response to onSelect (click triggers onSelect, then the prop change triggers onSelect again). Consider syncing internal state from the selectedIndex prop in the effect without calling onSelect (e.g., update selectedIndex state directly), so onSelect is reserved for user-initiated selection changes.

Suggested change
handleSelect(index);
setSelectedIndex(index);
setFocusedIndex(index);

Copilot uses AI. Check for mistakes.
Comment on lines +64 to 70
const prevIndex = useRef(index);
useEffect(() => {
handleSelect(index);
if (prevIndex.current !== index) {
prevIndex.current = index;
handleSelect(index);
}
}, [index]);
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

Please add/adjust unit coverage for this behavior (Vitest tests exist for EbayTabs): at minimum, assert onSelect is not called on initial mount, and (if selectedIndex is used in a controlled pattern) that a single user click results in a single onSelect call (no extra call from prop-sync). This will prevent regressions of the mount/double-fire issues.

Copilot generated this review using guidance from repository custom instructions.
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.

ui-core-react - ebay-tabs: "onSelect" is called on component render

3 participants