preactjs / signals

Manage state with style in every framework
https://preactjs.com/blog/introducing-signals/
MIT License
3.63k stars 88 forks source link

react-signals with next.js 14 #466

Closed czanbaka closed 3 months ago

czanbaka commented 6 months ago

Just curious if anyone has successfully gotten signals to work with next.js 14? I get this error when I try:

Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading 'length')

Call Stack areHookInputsEqual node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (10921:0) updateEffectImpl node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (12303:0) updateEffect node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (12323:0) updateSyncExternalStore node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (11802:0) Object.useSyncExternalStore node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (13448:0) useSyncExternalStore node_modules/next/dist/compiled/react/cjs/react.development.js (1797:0) P node_modules/@preact/signals-react/dist/signals.module.js (1:2148) Object.set [as current] node_modules/@preact/signals-react/dist/signals.module.js (1:2976) renderWithHooks node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (10968:0) updateFunctionComponent node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (16163:0) beginWork$1 node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (18359:0) beginWork node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (26741:0) performUnitOfWork node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (25587:0) workLoopConcurrent node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (25573:0) renderRootConcurrent node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (25529:0) performConcurrentWorkOnRoot node_modules/next/dist/compiled/react-dom/cjs/react-dom.development.js (24382:0) workLoop node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (261:0) flushWork node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (230:0) MessagePort.performWorkUntilDeadline node_modules/next/dist/compiled/scheduler/cjs/scheduler.development.js (534:0)

XantreDev commented 6 months ago

Automatic tracking for now is not working with next.js. You can use signals with my lib to allow signals tracking for components. I also working on swc transform plugin to allow use signals in next.js https://github.com/XantreGodlike/preact-signals/pull/45

JonAbrams commented 6 months ago

The new 2.0.0 version of @preact/signals-react provides a useSignals() hook that you can place in any component that reads a signal's value. It appears to work with Nextjs 14+ for me.

XantreDev commented 6 months ago

I've implemented swc plugin how to use

297

https://codesandbox.io/p/github/XantreGodlike/preact-signals-nextjs/main

yanayhollander commented 5 months ago

The @preact/signals-react documentation mention two ways:

  1. Adding a babel transform - didn't work for me in Next 14.0.1
    {
    "plugins": [["module:@preact/signals-react-transform"]]
    }
  2. call the useSignals() - work for me hook to make your components reactive, but a bit tedious calling it in every component.
XantreDev commented 5 months ago

For next JS you need to use SWC plugin

sanskar-19 commented 5 months ago

The @preact/signals-react documentation mention two ways:

  1. Adding a babel transform - didn't work for me in Next 14.0.1
{
  "plugins": [["module:@preact/signals-react-transform"]]
}
  1. call the useSignals() - work for me hook to make your components reactive, but a bit tedious calling it in every component.

Why not simply use the useSignal() hook to instantiate a signal

sanskar-19 commented 5 months ago

Consider a scenario below <button onClick={() => count.value++}> Value: {count}, value x 2 = {double} </button>

The above has been referred from the NPM documentation but, small difference is they referred signals as count.value and double.value in the JSX. It didn't seem to update the same in UI.

Not until I referred the signals as signal instead of signal.value in the JSX.

XantreDev commented 5 months ago

Consider a scenario below <button onClick={() => count.value++}> Value: {count}, value x 2 = {double} </button>

The above has been referred from the NPM documentation but, small difference is they referred signals as count.value and double.value in the JSX. It didn't seem to update the same in UI.

Not until I referred the signals as signal instead of signal.value in the JSX.

Reading signal .value field subscribes a component to it changes only if you are using babel plugin or used useSignals() on top of a component

sanskar-19 commented 5 months ago

So even when I am creating a signal using the useSignal() hook, still I have to manually call the useSignals()?

sanskar-19 commented 5 months ago

Consider a scenario below <button onClick={() => count.value++}> Value: {count}, value x 2 = {double} </button> The above has been referred from the NPM documentation but, small difference is they referred signals as count.value and double.value in the JSX. It didn't seem to update the same in UI. Not until I referred the signals as signal instead of signal.value in the JSX.

Reading signal .value field subscribes a component to it changes only if you are using babel plugin or used useSignals() on top of a component

Also then how using only signal instead of signal.value works.

sanskar-19 commented 5 months ago

Consider a scenario below <button onClick={() => count.value++}> Value: {count}, value x 2 = {double} </button> The above has been referred from the NPM documentation but, small difference is they referred signals as count.value and double.value in the JSX. It didn't seem to update the same in UI. Not until I referred the signals as signal instead of signal.value in the JSX.

Reading signal .value field subscribes a component to it changes only if you are using babel plugin or used useSignals() on top of a component

Also can you explain me the phrase subscribes a component to it/

XantreDev commented 5 months ago

So even when I am creating a signal using the useSignal() hook, still I have to manually call the useSignals()?

Conceptually react integration works like this. Every component wrapped with try finally to start tracking and finish it in finally block.

// start tracking reading of signals
const store = _useSignalsImplementation(1);
try {
  return renderYourComponent(props);
} finally {
  // stop tracking reading of signals
  store.f();
}

If you're using useSignals()

// start tracking and will stop tracking on new microtask
useSignals()
// code
return jsx
XantreDev commented 5 months ago

You can think about it like every component code wrapped in effect. If use are not using any react integration - there is no such effect

qingzhoufeihu commented 3 months ago

The @preact/signals-react documentation mention two ways:

  1. Adding a babel transform - didn't work for me in Next 14.0.1
{
  "plugins": [["module:@preact/signals-react-transform"]]
}
  1. call the useSignals() - work for me hook to make your components reactive, but a bit tedious calling it in every component.

image useSignal doesn't work for me, can't enter anything in the input field

qingzhoufeihu commented 3 months ago

@XantreGodlike Thanks for your plugin, it works well with @preact/signals-react in next.js14. image image image

XantreDev commented 3 months ago

image image It works, finally!

Setup is almost correct. But you should import signal, computed, effect and other functions from @preact-signals/safe-react not @preact/signals-react (to avoid undefined behaviour and reduce bundle size)

OpenSource03 commented 3 months ago

When I use signal() and return signal.value, it never gets updated on further signal.value updates. The only way to show it and update it is to just return signal, but in that case, if I have a complex object, such as User, I can't just do signal.username, I must do signal.value.username (which would not work as I said above, and as a bonus, if you return signal.value.something on a object, it always shows undefined)

qingzhoufeihu commented 3 months ago

When I use signal() and return signal.value, it never gets updated on further signal.value updates. The only way to show it and update it is to just return signal, but in that case, if I have a complex object, such as User, I can't just do signal.username, I must do signal.value.username (which would not work as I said above, and as a bonus, if you return signal.value.something on a object, it always shows undefined)

Maybe you need valtio.

XantreDev commented 3 months ago

When I use signal() and return signal.value, it never gets updated on further signal.value updates. The only way to show it and update it is to just return signal, but in that case, if I have a complex object, such as User, I can't just do signal.username, I must do signal.value.username (which would not work as I said above, and as a bonus, if you return signal.value.something on a object, it always shows undefined)

If you need deep reactivity you can use @preact-signals/utils

rschristian commented 3 months ago

@OpenSource03 Please provide an example.

That goes for everyone else too, without reproductions, we cannot help.

This thread has gotten pretty off topic so I'm going to close it out as answered.