Skip to content

Collection components and keyboard handling break with preact/compat (Preact 11) #10755

Open

Description

@mpsuszko

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] renders A X Y B.
  • Pressing ArrowDown on a focused Select trigger opens the list.

😯 Current Behavior

  1. Collections throw on mount: TypeError: <doc>.createElementNS is not a function.
  2. With the missing fake-DOM methods added (see the repro's ?shim=dom), [A, B] → [A, X, Y, B] renders A X B Y.
  3. 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:

  1. Missing fake-DOM methods. CollectionBuilder portals into the fake DOM in collections/Document.ts. Preact 11 creates nodes with parentDom.ownerDocument.createElementNS() and createTextNode() (preact/src/diff/index.js) and unmounts with node.remove() (preact/src/util.js). The fake DOM implements none of these.
  2. Elements look like comments. ElementNode reports nodeType = 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); in preact/src/diff/children.js). Every fake element looks like a comment, so the cursor becomes null and the remaining inserts are appended.
  3. The event spread drops native fields. createEventHandler spreads the event ({...e}). Preact passes native events, whose key, target and modifier keys are prototype getters, so the spread drops them. useTypeSelect then reads key.length on undefined.

I have a PR ready with unit tests. It makes these changes without changing behaviour under React:

  • Document gets createElementNS() and createTextNode(); ElementNode gets remove().
  • Only elements created via createElementNS() report ELEMENT_NODE. React only calls createElement() here, so React and the DevTools workaround are unaffected.
  • createEventHandler keeps 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.

  1. Open /. Both the ListBox and the Select show TypeError: … createElementNS is not a function.
  2. Open /?shim=dom. It adds only the missing fake-DOM methods, so that the next problems become visible.
  3. Click "Insert X, Y between A and B". The list shows A X B Y.
  4. 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions