Skip to content

Conversation

@SimYunSup
Copy link
Contributor

Related Issues

Fixes #371

Description of Changes

Change createSlot to useSlot hook and wrap useMemo

Checklist

Before submitting the PR, please make sure you have checked all of the following items.

  • The PR title follows the Conventional Commits convention. (e.g., feat, fix, docs, style, refactor, test, chore)
  • I have added tests for my changes.
  • I have updated the Storybook or relevant documentation.
  • I have added a changeset for this change. (e.g., for any changes that affect users, such as component prop changes or new features).
  • I have performed a self-code review.
  • I have followed the project's coding conventions and component patterns.

@changeset-bot
Copy link

changeset-bot bot commented Nov 27, 2025

🦋 Changeset detected

Latest commit: 24042b2

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

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

@vercel
Copy link

vercel bot commented Nov 27, 2025

@SimYunSup is attempting to deploy a commit to the goorm Team on Vercel.

A member of the Team first needs to authorize it.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @SimYunSup, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the internal mechanism for handling component slots by transitioning from a utility function (createSlot) to a dedicated React hook (useSlot). This change aims to enhance the performance and stability of various UI components by ensuring that slot elements are memoized, thereby preventing unnecessary re-renders. The new hook also standardizes the approach for managing default or fallback children, leading to a more consistent and optimized component architecture.

Highlights

  • Refactored Slot Mechanism: The createSlot utility has been replaced with a new useSlot React hook, centralizing and memoizing the logic for rendering children and fallback elements within components.
  • Component Migration: Numerous UI components, including Breadcrumb, Checkbox, Dialog, Menu, MultiSelect, NavigationMenu, Popover, Radio, Select, Sheet, Switch, and Tooltip, have been updated to utilize the new useSlot hook.
  • Performance Improvement: The useSlot hook internally leverages useMemo to prevent unnecessary re-renders of slot elements, contributing to improved component performance and stability.
  • Enhanced Testing: A new test case has been added for the Sheet component to specifically verify that its internal state, such as active tabs, is correctly preserved across re-renders, validating the benefits of the memoized slot implementation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

이 PR은 createSlot을 메모이제이션이 적용된 useSlot 훅으로 변경하여 성능을 개선하려는 좋은 의도를 가지고 있습니다. 하지만 현재 useSlot 훅의 구현과 사용 방식에 몇 가지 중요한 문제가 있어, 의도한 대로 동작하지 않거나 오히려 성능 저하를 일으킬 수 있습니다.

주요 문제는 useSlot 내부의 useMemo 의존성 배열이 불완전하고, useSlot을 호출할 때 인라인 JSX를 전달하여 메모이제이션이 깨지는 것입니다.

자세한 내용은 각 파일에 남긴 리뷰 코멘트를 참고해주세요. 이 문제들을 해결하면 PR의 본래 목적인 성능 최적화를 달성할 수 있을 것입니다.


const { size } = useBreadcrumbContext();
const IconElement = createSlot(children || <SlashOutlineIcon size="100%" />);
const IconElement = useSlot(children, <SlashOutlineIcon size="100%" />);
Copy link
Contributor

Choose a reason for hiding this comment

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

high

useSlot에 인라인 JSX를 fallback 인자로 전달하면 useSlot 내부의 useMemo가 매번 다시 실행되어 메모이제이션이 깨집니다. 이는 이 PR의 목적인 성능 최적화에 반하는 동작입니다.

fallback으로 사용되는 <SlashOutlineIcon size="100%" />는 props가 변경되지 않으므로, 컴포넌트 외부에 상수로 선언하여 useSlot에 전달하면 이 문제를 해결할 수 있습니다.

// 예시
const slashIcon = <SlashOutlineIcon size="100%" />;

export const BreadcrumbSeparator = forwardRef<HTMLLIElement, BreadcrumbSeparator.Props>(
    (props, ref) => {
        // ...
        const IconElement = useSlot(children, slashIcon);
        // ...
    },
);

이 패턴은 이 파일의 183번째 줄과 다른 컴포넌트 파일들에서도 동일하게 발견됩니다. 모든 useSlot 사용 부분을 검토하여 인라인 JSX 대신 안정적인 참조를 사용하도록 수정해주세요.

const dataAttrs = createDataAttributes({ invalid });

const ThumbElement = useMemo(() => createSlot(<SwitchThumbPrimitive />), []);
const ThumbElement = useSlot(<SwitchThumbPrimitive />);
Copy link
Contributor

Choose a reason for hiding this comment

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

high

기존의 useMemo(() => createSlot(...), []) 코드를 useSlot(<SwitchThumbPrimitive />)으로 변경하면서 성능 저하가 발생했습니다.

useSlot 훅에 인라인 JSX 요소인 <SwitchThumbPrimitive />를 직접 전달하면, SwitchRoot 컴포넌트가 리렌더링될 때마다 새로운 요소 참조가 생성됩니다. 이로 인해 useSlot 내부의 useMemo가 매번 다시 실행되어 ThumbElement 컴포넌트가 계속해서 재생성됩니다. 이는 상태를 잃게 만들고, 이 PR의 목적인 메모이제이션의 이점을 무효화시킵니다.

useSlot의 인자로 전달되는 JSX를 useMemo로 감싸서 안정적인 참조를 유지하도록 수정해야 합니다.

Suggested change
const ThumbElement = useSlot(<SwitchThumbPrimitive />);
const ThumbElement = useSlot(useMemo(() => <SwitchThumbPrimitive />, []));

Copy link
Contributor

Choose a reason for hiding this comment

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

2025-12-03.9.57.44.mov
// switch.stories.tsx
<Switch.Root checked={checked1} onCheckedChange={setChecked1} {...args} />

The first Switch component in the <Default /> story of switch.stories.tsx does not animate smoothly.

export const SwitchRoot = forwardRef<HTMLButtonElement, SwitchRoot.Props>((props, ref) => {
// ...

const ThumbElement = useSlot(<SwitchThumbPrimitive />);
const children = childrenProp || <ThumbElement />;

In the above code, if childrenProp is absent, it always renders ThumbElement (SwitchThumbPrimitive). Inside useSlot, it memoizes the Slot component by depending on _children (SwitchThumbPrimitive). However, inline JSX always creates a new object. Therefore, when childrenProp is absent, a new component is created each time, making it appear as if the animation isn't applied.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for testing! I changed code to memoize rendered element.

@vercel
Copy link

vercel bot commented Dec 3, 2025

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

Project Deployment Preview Comments Updated (UTC)
vapor-ui Ready Ready Preview Comment Dec 3, 2025 1:22am

@SimYunSup
Copy link
Contributor Author

Close because #389 is integrated PR.

@SimYunSup SimYunSup closed this Dec 16, 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.

Bug(Sheet): Uncontrolled tab in sheet is reinitializing when re-render

3 participants