-
Notifications
You must be signed in to change notification settings - Fork 11
React 19 upgrade #4428
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
React 19 upgrade #4428
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
86212dc
Add React 19 internal-API migration plan
lbwexler f0e009a
Reframe plan as React 18-safe React 19 prep (findDOMNode only)
lbwexler 8b12559
Add Popover (PopoverNext) and floating-ui phases as React 18-safe prep
lbwexler 86a2868
Phase 1: remove findDOMNode from HoistInputModel (React 19 prep)
lbwexler 01d2dd8
Move React 19 findDOMNode changelog entry to 87.0.0-SNAPSHOT
lbwexler 0b0e4d7
Phase 2: migrate Popover kit wrapper to Blueprint PopoverNext (React …
lbwexler 483c8c8
Phase 3: migrate mobile Popover from react-popper to Floating UI (Rea…
lbwexler 1f50d2c
Update menuPositionToPlacement doc comment for Floating UI
lbwexler 9b0ed20
Apply forward-compatible React 19 type-prep fixes
lbwexler 1de86c1
Bump to React 19 (deps + peer range)
lbwexler 41881de
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler a77b60b
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler 9f942c2
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler 05f8190
Required Mobile fixes
lbwexler e024f62
Required Mobile fixes
lbwexler 49337a1
Checkpoint
lbwexler 63ac191
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler 6702936
feedback from tom tirapani
lbwexler f3b121b
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler 073c9b0
Remove extra comment
lbwexler 6a04f99
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler 509afa9
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler 1b9eb98
update yarn.lock
lbwexler 9e8f292
update yarn.lock
lbwexler 2e4f540
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler c581537
update yarn.lock
lbwexler d358602
update changelog for release
lbwexler e026ce0
Final pre-merge tweaks
lbwexler 49ae59f
Fix regression to Filter Chooser.
lbwexler be5fbf2
Merge remote-tracking branch 'origin/develop' into react-19-upgrade
lbwexler 4edf23c
Merge branch 'develop' into react-19-upgrade
lbwexler File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| # React 19 upgrade | ||
|
|
||
| **Branch:** `react-19-upgrade` (based on `develop`) — see [#4205](https://github.com/xh/hoist-react/issues/4205). | ||
|
|
||
| **Outcome:** hoist-react now type-checks and runs against React 19. This is a clean cut — the | ||
| `react` / `react-dom` peer dependency moves to `^19.2.0` and React 18 is no longer supported. | ||
| Consuming apps upgrade to React 19 alongside this Hoist major. | ||
|
|
||
| ## Context | ||
|
|
||
| A scan of hoist-react for React-19-affected internal APIs found the surface small, and most of it | ||
| was already handled before this branch: | ||
|
|
||
| - `ReactDOM.render` / `hydrate` / `unmountComponentAtNode` — **none** (already on `createRoot`, in | ||
| `appcontainer/AppContainerModel.ts` and `desktop/cmp/dash/container/DashContainerModel.ts`). | ||
| - legacy Blueprint `Overlay` — already migrated to `Overlay2` in `kit/blueprint/Wrappers.ts`. | ||
| - `defaultProps` on function components — none. | ||
| - Legacy context (`childContextTypes` / `getChildContext`) — none. | ||
| - String refs / `element.ref` access / `props.ref` reads — none. | ||
| - `cloneElement` is used in several places but never to forward a `ref`, so the React 19 | ||
| `cloneElement` ref-handling change does not apply. | ||
|
|
||
| That left the work below: two removed/broken APIs (`findDOMNode`, Popper.js-based popovers), the | ||
| custom-element boolean-prop change that affects the Onsen kit, the types/peer bump, and the | ||
| type-only fallout from `@types/react@19`. | ||
|
|
||
| ## Types / peer bump | ||
|
|
||
| - `package.json`: `peerDependencies.react` / `react-dom` → `^19.2.0` (React 18 dropped); | ||
| dev `react` / `react-dom` → `^19.2.0`; `@types/react` / `@types/react-dom` and their | ||
| `resolutions` pins → `19.x`. | ||
| - Removed `react-popper` as a direct dependency; added `@floating-ui/react` (see below). | ||
|
|
||
| ## `findDOMNode` removal | ||
|
|
||
| `findDOMNode` is removed in React 19. `HoistInputModel.domEl` (`cmp/input/HoistInputModel.ts`) | ||
| used it as a fallback for when `domRef.current` resolved to a class instance rather than a DOM | ||
| element. A trace of all desktop + mobile HoistInput implementations confirmed every one roots | ||
| `domRef` on a DOM element, so the fallback was dead defensive code. `domEl` now resolves the | ||
| element directly from the ref; the `findDOMNode` / `ReactInstance` imports are dropped. | ||
|
|
||
| ## Popovers off Popper.js → Floating UI | ||
|
|
||
| Both popover implementations relied on Popper.js, which is React-18-capped and not React 19 | ||
| compatible. | ||
|
|
||
| - **Desktop kit wrapper — `kit/blueprint/Wrappers.ts`.** Blueprint's legacy `Popover` relies on | ||
| the removed `findDOMNode`; its Floating UI-based `PopoverNext` (same Blueprint build) is the | ||
| replacement — mirroring the existing `Overlay2 as Overlay` pattern. The wrapper now renders | ||
| `PopoverNext` and runs incoming props through Blueprint's `popoverPropsToNextProps()` helper, | ||
| which maps `position` / `modifiers` / `minimal` / `boundary` and preserves the legacy | ||
| `shouldReturnFocusOnClose` default. Because the helper handles the mapping, the `popover` | ||
| factory keeps its existing `PopoverProps` API and **no call sites needed edits**. | ||
| - **Mobile — `mobile/cmp/popover/Popover.ts`** (the sole `react-popper` consumer). Swapped | ||
| `usePopper` for `@floating-ui/react`'s `useFloating`, reusing the Floating UI copy Blueprint's | ||
| `PopoverNext` already pulls in rather than adding a separate library. Positioning uses | ||
| `middleware: [autoPlacement() | flip(), shift({padding: 10})]` with `whileElementsMounted: | ||
| autoUpdate`; placement names are shared with Popper.js so `menuPositionToPlacement` is | ||
| unchanged. Element wiring uses Floating UI's own `refs.setReference` / `refs.setFloating` | ||
| callback refs rather than the previous observable refs — the MobX-observer re-render the old | ||
| approach depended on did not reliably fire under React 19 for the portaled content. The obsolete | ||
| Popper.js-specific `popperOptions` escape-hatch prop was removed (breaking change; unused in | ||
| Hoist). | ||
|
|
||
| ## Onsen custom-element boolean props — `kit/onsen/index.ts` | ||
|
|
||
| react-onsenui encodes boolean props as the string `''` (or `null`). Under React 18 these were | ||
| assigned as DOM *attributes*; React 19 assigns them as DOM *properties* on the underlying custom | ||
| element, and Onsen's boolean property setters treat `''` as falsy — so props like `checked`, | ||
| `disabled`, and `visible` silently failed to apply. The kit wrapper now strips boolean props from | ||
| the rendered props and applies the real booleans imperatively via a ref in `useLayoutEffect` | ||
| (accounting for Onsen's deprecated aliases `isOpen`/`isCancelable`/`isDisabled`), routing through | ||
| Onsen's own setters. | ||
|
|
||
| ## `@types/react@19` type adjustments | ||
|
|
||
| Type-only fallout, no runtime behavior change: | ||
|
|
||
| - `useRef` now requires an initial arg — `desktop/cmp/tab/dynamic/DynamicTabSwitcher.ts` | ||
| (`useRef<HTMLDivElement>(null)`). | ||
| - Ref-callback return values are treated as cleanup functions — `desktop/cmp/input/CodeInput.ts` | ||
| wraps its `createCodeEditor` ref callback to return `void` (the method is `async`, so it would | ||
| otherwise return a Promise that React invokes as a destructor). | ||
| - `ReactElement.props` is typed `unknown` — `desktop/cmp/dash/canvas/widgetchooser/DashCanvasWidgetChooser.ts` | ||
| narrows icon `props` before reading `iconName`. | ||
| - Widened FC return type / stricter element typing — casts in `cmp/grid/columns/Column.ts` and | ||
| `desktop/cmp/tab/dynamic/scroller/Scroller.ts` (`as ReactElement`), and `ReactElement<any>` in | ||
| `desktop/hooks/UseContextMenu.ts` and `desktop/hooks/UseHotkeys.ts`. | ||
|
|
||
| ## Not done (still valid on React 19) | ||
|
|
||
| - **`forwardRef` → `ref`-as-prop.** `forwardRef` is *deprecated* in React 19, not removed — it | ||
| still works, so it never blocked this upgrade. Left in place as future modernization cleanup; | ||
| sites include `core/HoistComponent.ts` (the framework-wide `cfg.isForwardRef` ref wrap — the | ||
| delicate one), `cmp/grid/columns/Column.ts`, and `kit/onsen/index.ts`. | ||
| - **`propTypes`** — React 19 ignores them (not an error). No runtime `propTypes` definitions exist | ||
| in the codebase; only a stale comment reference in `desktop/cmp/button/index.ts`. | ||
|
|
||
| ## Verification | ||
|
|
||
| - `tsc --noEmit` clean against `@types/react@19`. | ||
| - `yarn lint`. | ||
| - Smoke in toolbox via `yarn startWithHoist`: | ||
| - inputs and anything reading `HoistInputModel.domEl` (focus, autofocus, sizing/measurement); | ||
| - desktop popovers: Select, DateInput, combos, column chooser, ViewManager menu, mobile | ||
| MenuButton — placement, dismissal, and focus-return behavior; | ||
| - mobile Popover positioning, flip/shift near viewport edges, scroll/resize updates; | ||
| - Onsen-backed mobile controls with boolean state (checkboxes, switches, dialog visibility). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.