[EuiPortal] Migrate to function component#9598
Conversation
There was a problem hiding this comment.
Pull request overview
Refactors EuiPortal (core utility used by overlays) from a class component to a hook-based function component, aiming to preserve existing portal insertion/removal behavior and improve React Strict Mode cleanup behavior.
Changes:
- Converted
EuiPortalfrom a class component to a memoized function component usinguseState/useContext. - Replaced
componentDidMount/componentWillUnmountwith an isomorphicuseLayoutEffect/useEffectpattern to create/cleanup the portal node. - Removed the previously exported
EuiPortalClassimplementation.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export const EuiPortal: FunctionComponent<EuiPortalProps> = memo((_props) => { | ||
| const props = usePropsWithComponentDefaults('EuiPortal', _props); | ||
| const { children, insert, portalRef } = props; |
There was a problem hiding this comment.
This refactor removes the previously exported EuiPortalClass symbol from this module. Even if it wasn't re-exported from components/portal/index.ts, consumers can still deep-import it from components/portal/portal, so this is a potential breaking API change and also conflicts with the PR description of “no API changes”. Either keep a backwards-compatible export (e.g., a deprecated alias) or explicitly call this out as an API/breaking change.
There was a problem hiding this comment.
The class component was exported from the file, but not from the portal folder.
If someone imported it manually, it would be rather unexpected usage. The only officially exported component was EuiPortal. Imho, this doesn't require marking it as breaking change.
tkajtoch
left a comment
There was a problem hiding this comment.
Code changes look great! I tested portalled components with and without StrictMode, and used the provided CodeSandbox links to test the same in React 19. Everything works as expected, and the attached Kibana CI run assures me that everything's safe.
|
ℹ️ To be on the safe side I'll run it in Kibana CI (CI run) again, considering that the last run has been some time ago already. |
💚 Build SucceededHistory
cc @mgadewoll |
💚 Build Succeeded
History
cc @mgadewoll |
The CI run finished 🟢 |
Summary
closes #9456
closes #8784
This PR refactors
EuiPortalfrom a class component to a function component.API Changes
⚪ no API changes
Screenshots
Screen.Recording.2026-04-17.at.13.51.42.mov
Screen.Recording.2026-04-17.at.13.52.13.mov
Impact Assessment
Note: Most PRs should be tested in Kibana to help gauge their Impact before merging.
Impact level: 🟢 None
The refactor is a 1:1 functional and structural migration. There should not be any impact.
ℹ️ The changes were run in Kibana CI (🟢 CI build)
Release Readiness
QA instructions for reviewer
💻 EuiPortal storybook
💻 React 18 strict mode testing codesandbox
💻 React 19 strict mode testing codesandbox
strictMode: truehere or spin up your own react app)EuiToolTipdoes not work (doesn't show up) in React 18 strict mode, it'll be fixed by [EuiToolTip] Refactor to a functional component, memoize style and improve tests #9570Checklist before marking Ready for Review
QA: Tested docs changesChangelog: Added changelog entryBreaking changes: Addedbreaking changelabel (if applicable)Reviewer checklist