Skip to content
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

[React 19] - ReactElement created from React.createElement are not renderable anymore #31012

Closed
SukkaW opened this issue Sep 20, 2024 · 3 comments

Comments

@SukkaW
Copy link

SukkaW commented Sep 20, 2024

Summary

TL; DR

Using React.createElement in the JSON.parse's revive function to deserialize json into ReactElement, but the returned value can no longer be rendered with errors:

Error: Objects are not valid as a React child (found: object with keys {key, props, _owner, _store}). If you meant to render a collection of children, use an array instead.

I am maintaining a docs site powered by React & Next.js Pages Router.

The docs site copy-pastes a risk approach of rendering MDX on the server serializing MDX components on the server and deserializing on the client from the React docs site:

https://github.com/reactjs/react.dev/blob/39abc60fce1588b4e83cc45d52b522aa63c01bc2/src/utils/compileMDX.ts#L145C1-L160C4
https://github.com/reactjs/react.dev/blob/39abc60fce1588b4e83cc45d52b522aa63c01bc2/src/pages/%5B%5B...markdownPath%5D%5D.js#L104-L128

I have slightly modified the reviveNodeOnClient function to use React.createElement instead:

function reviveNodeOnClient(key: unknown, val: unknown) {
  if (Array.isArray(val) && val[0] === '$r') {
    // Assume it's a React element.
    let type = val[1];
    const key = val[2];
    let props = val[3];
    if (type === 'wrapper') {
      type = Fragment;
      props = { children: props.children };
    }
    if (type in MDXComponents) {
      type = MDXComponents[type];
    }
    if (!type) {
      if (process.env.NODE_ENV !== 'production') {
        // eslint-disable-next-line no-console -- log error
        console.warn(`Unknown type: ${type}`);
      }
      type = Fragment;
    }
-    return {
-      $$typeof: Symbol.for('react.element'),
-      type: type,
-      key: key,
-      ref: null,
-      props: props,
-      _owner: null,
-    };
+    return createElement(type, key ? Object.assign(props, { key }) : props);
  }
  return val;
}

Yet, I get this error:

Error: Objects are not valid as a React child (found: object with keys {key, props, _owner, _store}). If you meant to render a collection of children, use an array instead.

Reproduction

Repo SukkaW/mirrorz-help with branch housekeeping.

https://github.com/SukkaW/mirrorz-help/tree/housekeeping

@eps1lon
Copy link
Collaborator

eps1lon commented Sep 25, 2024

You probably have different versions of React installed. Or a 3rd party library is bundling the JSX runtime of a different React version.

The repro is too big for us to check out. Can you trim it down to something minimal?

@SukkaW
Copy link
Author

SukkaW commented Sep 25, 2024

You probably have different versions of React installed

I don't know. Since it is using Next.js Pages Router, Next.js would most likely not use its bundled version of React.

Or a 3rd party library is bundling the JSX runtime of a different React version.

No, we do not use any CSS-in-JS that requires non-React jsx runtime. It uses style9, a similar AoT CSS-in-JS approach to Facebook's stylex.

The repro is too big for us to check out. Can you trim it down to something minimal?

Lemme give it a shot!

@SukkaW
Copy link
Author

SukkaW commented Sep 25, 2024

Update

I know what happened and it is not an issue of React.

So the serialization assumes the $$typeof would most likely be Symbol.for('react.element'), which is true in 2018 (when Dan implements that serialization process for the https://react.dev).

Years later, the coming React 19 is introducing new shapes of react element (#28813), and therefore objects with Symbol.for('react.transitional.element') are produced. The serialization process doesn't handle Symbol.for('react.transitional.element'), hence the crash.

Also, because the original $$typeof information is lost during the serialization process, the error A React Element from an older version of React was rendered was never reported.

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

No branches or pull requests

2 participants