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

Cannot set property createElement of [object Module] which has only a getter #429

Open
Draecal opened this issue Nov 1, 2023 · 17 comments
Labels

Comments

@Draecal
Copy link

Draecal commented Nov 1, 2023

Error: Cannot set property createElement of [object Module] which has only a getter

Environment:

  • Nextjs14
  • AppRouter
  • src folder
  • Tailwindcss

Code:

import { GeistSans, GeistMono } from 'geist/font'
import { signal } from '@preact/signals-react'
import './globals.css'

export const toOpen = signal(false)
export const openDialog = () => { toOpen.value = true ; console.log(toOpen)}

export const Register = () => {
  return (<>
    <dialog open={toOpen.value}>
      <p>Create a new account</p>
    </dialog>
  </>
  )
}

standard boilerplate code follows

Expected:

At least the app to load, instead receiving the error described.

Tried all approaches possible, even the documentation examples within my environment.

@ghost2023
Copy link

I am having the similar error. The error on the terminal says "cannot set property of jsx". Anything new?

@d-zheng
Copy link

d-zheng commented Nov 3, 2023

I am running into the error as well when I try to use signals in a Nextjs13 project. When 'reactStrictMode' is set to false, signal seems able to continue working even with the error. Not sure if it is Next specific, but the issue was not observed in React projects.

@d-zheng
Copy link

d-zheng commented Nov 4, 2023

Spent a bit time on it today, and found out the error likely related to Next.js's pre-rendering feature (https://nextjs.org/learn-pages-router/basics/data-fetching/pre-rendering). Pre-rendering happens at build time (for production) or at each page request (for development). I've confirmed the error doesn't show up at build time if I replace 'next build' with 'next experimental-compile' which is a new command that skips the pre-rendering step. I didn't see this error either when I took dynamic import approach with { ssr : false } option for a component including signals.

@ghost2023
Copy link

ghost2023 commented Nov 4, 2023

So basically don't use signals in nextjs.
Sad

@JonAbrams
Copy link

JonAbrams commented Nov 5, 2023

I don't think there's any need for signals to hook into React for SSR, since the state will never change server-side. Therefore, it should be enough (🤞) for signals-react to not call installJSXHooks: https://github.com/preactjs/signals/blob/main/packages/react/runtime/src/auto.ts#L339

Unfortunately, I don't know enough about either Signal's or Next's codebase to make that happen.

To help people googling find this issue, here's my error: TypeError: Cannot set property jsx of [object Module] which has only a getter

Related: vercel/next.js/issues/45054

Added a workaround to that issue.

@d-zheng
Copy link

d-zheng commented Nov 6, 2023

Thanks. I tried the workaround and it does the job. But I am seeing a side-effect that it voids the rendering optimization features of Signals, i.e., those components that are not subscribing to a signal would be also re-rendered when the signal value changes 😞

@JonAbrams
Copy link

Try the library I just published on npm (signals-react-safe), it fixes the re-rendering issue.

@d-zheng
Copy link

d-zheng commented Nov 6, 2023

@JonAbrams, thanks for the fix, it was quick.👍 Yeah, it is working most of the part now: the original error is gone ✔️, and only text node is updated when signal updates without re-rendering the component✔️. The only remaining one is when signal passed into a component as prop, the component doesn't get re-rendered (if referring to signal value in text node) when signal updates; referring to signal works though. Sorry, I am new to React/Next/Signals, maybe I missed anything? Anyway, the library with the fix provides pretty much everything of Signals I need for Next. Thanks again!

@JonAbrams
Copy link

JonAbrams commented Nov 6, 2023 via email

@d-zheng
Copy link

d-zheng commented Nov 6, 2023

Works like a charm! 👍

@zeropaper
Copy link

zeropaper commented Nov 16, 2023

Or just add "use client" at the top of the file that imports the signal.

@hdwv
Copy link

hdwv commented Nov 17, 2023

To have the component re-render when a signal updates, use the useSignalValue hook I added.

I tried to use this in a nextjs page without luck. The signal I want to use is to hold the current lang value

// useI18n.tsx
import { signal, useSignalValue } from 'signals-react-safe'

export const language = signal<Languages>('en') // I export a signal here

// the translate fn just return a json object

export const useI18n = () => {
  return translate(languages[useSignalValue(language)])
}
// app/events/page.tsx

import React from 'react'
import { useI18n } from '@/app/ui/hooks/useI18n'

const Page = () => {
    const t = useI18n()

    return (
        <section>
             {t('CREATE_EVENT')}
        </section>
    )
}

export default Page

The error when this page renders on the server:

 node_modules/signals-react-safe/dist/index.mjs (15:37) @ signal2
 ⨯ useState only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/react-client-hook-in-server-component
    at useI18n (./app/ui/hooks/useI18n.tsx:28:98)
    at Page (./app/events/page.tsx:18:77)
    at stringify (<anonymous>)

@JonAbrams
Copy link

JonAbrams commented Nov 17, 2023

Under the hood useSignalValue uses useState. So it means you can only use it in client components. I should look into if it's possible to avoid using useState in server components to fix this.

@JonAbrams
Copy link

Actually, it turns out you're not supposed to use hooks in server components. Instead, access the signal and/or it's value directly in the server component. Since server components don't re-render, it should be safe.

In the future, please raise an issue with the signals-react-safe library here.

e.g.

import { language } from '../mySignals';

const Page = () => {
    const t = translate(languages[language.value]);

    return (
        <section>
             {t('CREATE_EVENT')}
        </section>
    )
}

@QingjiaTsang
Copy link

it seems like you guys seeing this error at the server side component, but I use it literally at client side components but not with the props drilling way, just the export and import way, and the error occurs...

@augustjk
Copy link

augustjk commented Mar 9, 2024

The error appears to stem from trying to patch React.createElement.

React.createElement = wrapJsx(React.createElement);

There seems to be some inconsistent behavior on webpack module resolution with ES module interop in how import React from 'react' or import * as React from 'react' is converted to require('react') calls. The same is true for the runtime JSX module patching.

We've noticed our patching of React.createElement started failing with the same error shown in original post in next@14. I managed to workaround that by patching like

(React.default || React).createElement = wrap(React.createElement);

or

Object.assign(React.default || React, {createElement: wrap(React.createElement)});

See https://github.com/lit/lit/pull/4575/files#diff-c058cf28cfeb3924265a8702a1c831cc7b6412b860c404754e9f99791287e797

@XantreDev
Copy link
Contributor

This is a problem related with monkeypatching from @preact/signals-react versions 1.x.x. Now you can use babel plugin (but not all feature of next.js working with it). Alternatively - you can @preact-signals/safe-react with swc plugin

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

No branches or pull requests

10 participants