Repository navigation
Collection components and keyboard handling break with preact/compat (Preact 11) #10755
Description
Provide a general summary of the issue here
In an app that aliases react / react-dom to preact/compat (Preact 11), React Aria Components collections (ListBox, Select, GridList, …) throw on mount. Once that is worked around, items render in the wrong order after an update, and keyboard handlers wrapped by createEventHandler get events without key.
🤔 Expected Behavior?
- Collection components render.
- Inserting items keeps the order:
[A, B]→[A, X, Y, B]rendersA X Y B. - Pressing ArrowDown on a focused Select trigger opens the list.
😯 Current Behavior
- Collections throw on mount:
TypeError: <doc>.createElementNS is not a function. - With the missing fake-DOM methods added (see the repro's
?shim=dom),[A, B]→[A, X, Y, B]rendersA X B Y. - ArrowDown on the Select trigger does nothing, and each keydown throws
TypeError: Cannot read properties of undefined (reading 'length').
💁 Possible Solution
All three causes are in react-aria:
- Missing fake-DOM methods.
CollectionBuilderportals into the fake DOM incollections/Document.ts. Preact 11 creates nodes withparentDom.ownerDocument.createElementNS()andcreateTextNode()(preact/src/diff/index.js) and unmounts withnode.remove()(preact/src/util.js). The fake DOM implements none of these. - Elements look like comments.
ElementNodereportsnodeType = 8(comment). After placing a child, Preact moves its insertion cursor to the next sibling and skips comment nodes (while ((oldDom = oldDom && oldDom.nextSibling) && oldDom.nodeType == 8);inpreact/src/diff/children.js). Every fake element looks like a comment, so the cursor becomesnulland the remaining inserts are appended. - The event spread drops native fields.
createEventHandlerspreads the event ({...e}). Preact passes native events, whosekey,targetand modifier keys are prototype getters, so the spread drops them.useTypeSelectthen readskey.lengthonundefined.
I have a PR ready with unit tests. It makes these changes without changing behaviour under React:
DocumentgetscreateElementNS()andcreateTextNode();ElementNodegetsremove().- Only elements created via
createElementNS()reportELEMENT_NODE. React only callscreateElement()here, so React and the DevTools workaround are unaffected. createEventHandlerkeeps the spread and falls back to the original event for fields the spread dropped.
🔦 Context
We build a design system on React Aria Components for micro-frontends. Most of them alias React to Preact for bundle size, so none of them can use collection components or keyboard navigation today. I know full Preact support was closed as out of scope (#781). This issue is only about these three root causes, which seem small and safe to fix.
🖥️ Steps to Reproduce
Repro: https://stackblitz.com/~/github.com/mpsuszko/react-aria-preact-repro (source: https://github.com/mpsuszko/react-aria-preact-repro)
The repro is a Vite app with react-aria-components 1.22.1 and preact 11.0.1, using @preact/preset-vite.
- Open
/. Both the ListBox and the Select showTypeError: … createElementNS is not a function. - Open
/?shim=dom. It adds only the missing fake-DOM methods, so that the next problems become visible. - Click "Insert X, Y between A and B". The list shows
A X B Y. - Tab to the Select button and press ArrowDown. The list does not open, and the console shows
Cannot read properties of undefined (reading 'length').
With the proposed change applied to react-aria 3.53.1, the repro renders A X Y B, ArrowDown opens the list, and no errors are logged.
Version
react-aria-components 1.22.1 (react-aria 3.53.1); preact 11.0.1
What browsers are you seeing the problem on?
Chrome
If other, please specify.
No response
What operating system are you using?
macOS
🧢 Your Company/Team
No response
🕷 Tracking Issue
No response
Activity