Skip to content

Bug: Can't hydrate useOpaqueIdentifier generated object in another component #20127

Closed
@diegohaz

Description

@diegohaz

React version: experimental

I found this bug while trying out useOpaqueIdentifier on a component that uses aria-activedescendant. It works great on a CRA app, but it breaks on server rendered apps.

The way it's implemented is that each item registers its id into a state up in the tree so the container element can get the active id and render it on the aria-activedescendant prop.

Steps To Reproduce

  1. Create a simple server rendered app (for example, using Next.js).
  2. Create a component that uses React.unstable_useOpaqueIdentifier and passes the generated object to a component up in the tree through callbacks.
  3. Try to render the generated ID to a prop in the ancestor component.

Link to code example:

https://codesandbox.io/s/react-useopaqueidentifier-aria-activedescendant-bug-geutb?file=/pages/index.js:0-412

The current behavior

It throws an error:

Unhandled Runtime Error
Error: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.

Call Stack
Object.readValue
node_modules/react-dom/cjs/react-dom.development.js (16555:0)
diffProperties
node_modules/react-dom/cjs/react-dom.development.js (9475:0)
prepareUpdate
node_modules/react-dom/cjs/react-dom.development.js (10352:0)
updateHostComponent$1
node_modules/react-dom/cjs/react-dom.development.js (20070:0)
completeWork
node_modules/react-dom/cjs/react-dom.development.js (20236:0)
completeUnitOfWork
node_modules/react-dom/cjs/react-dom.development.js (23738:0)
performUnitOfWork
node_modules/react-dom/cjs/react-dom.development.js (23710:0)
workLoopSync
node_modules/react-dom/cjs/react-dom.development.js (23618:0)
renderRootSync
node_modules/react-dom/cjs/react-dom.development.js (23577:0)
performConcurrentWorkOnRoot
node_modules/react-dom/cjs/react-dom.development.js (22968:0)
workLoop
node_modules/scheduler/cjs/scheduler.development.js (590:0)
flushWork
node_modules/scheduler/cjs/scheduler.development.js (545:0)
MessagePort.performWorkUntilDeadline
node_modules/scheduler/cjs/scheduler.development.js (157:0)

I believe it's triggered here:

// If we encounter useOpaqueReference's opaque object, this means we are hydrating.
// In this case, call the opaque object's toString function which generates a new client
// ID so client and server IDs match and throws to rerender.
nextProp.toString();

The expected behavior

It works well when not using SSR:

https://codesandbox.io/s/react-useopaqueidentifier-aria-activedescendant-y3f22

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions