8 ms·
Preact Signals
- nsonha 4y agocan this be reused in nodejs? Im'm not gonna waste my time when there arre Rx and Mobx. They are just pure reactive programming library instead of trying to be a front-end framework
- mhagemeister 4y agoPreact maintainer here. Yes, signals can be used independently of any framework. It runs everywhere JavaScript runs. To use it in node without any framework adapters, import the `@preact/signals-core` package, which just exports the reactivity API.
- brundolf 4y agoVery similar model to MobX. Main difference seems to be that deep accesses/mutations aren't (?) tracked, which is unfortunate. But it does mean there's a little less magic going on. In any case, I'm glad to see more people doing this style of reactivity
- stanislavb 4y agoI guess it's similar to this MobX :)? https://github.com/mobxjs/mobx https://github.com/mobxjs/mobx
- fabiospampinato 4y agoDeep reactivity can be implemented on top of the provided functions, I have a version of that for Preact that doesn't seem super broken here: https://codesandbox.io/s/sparkling-cherry-bn9bu5?file=/src/index.js https://codesandbox.io/s/sparkling-cherry-bn9bu5?file=/src/i...
- nikivi 4y agoDon't get why you would use it over Solid (https://www.solidjs.com https://www.solidjs.com). I guess to keep compat with React ecosystem but moving React components to Solid is trivial. Solid signals have better DX & I think are more performant.
- cageface 4y agoI recently ported my side project app from React to Solid and it was surprisingly straightforward. I think this is an advantage of Solid over Svelte. The concepts and syntax are quite similar and it eliminates the manual dependency tracking headaches from React.
- skybrian 4y agoI just browsed the Solid website for the first time. It looks somewhat interesting, but one thing I noticed is that rollup seems to be the preferred way to do builds? I kind of like keeping things simple by using just Preact and esbuild.
- fabiospampinato 4y agoSolid uses a custom Babel transform. Interestingly performance and simple build process aren't mutually exclusive, you can get both, the transform provides (arguably) convenience features mainly.
- iainmerrick 4y agoI recently started using Solid myself, and I was initially skeptical about that too. I love esbuild as it’s simple and fast, and distrust all these other slow and complex JS tools, especially Webpack. But it actually uses Rollup and esbuild by default (via Vite): Rollup for the HTML/CSS and esbuild for the JS/JSX. It works great and it’s plenty fast.
- eyelidlessness 4y agoYou can use Solid without any build tooling (with some caveats). I’m surprised Rollup is prominent at all on the site. The preferred tooling for Solid is its custom Babel plugin, but you can skip it and use hyperscript or html tagged templates. From what I’ve seen on GitHub they’re generally moving towards recommending Vite and their Vite starter is as simple as you can get (far simpler than ESBuild projects I maintain, there isn’t even a need for config or CLI flags or anything), but there’s an ESBuild plugin available. Ultimately all current solutions just wrap the Babel plugin in some form.
- rektide 4y agoMinor aside, I really dig that there is a synchronous `signal.peek()`. Common accepted dogma is that an async value is ought only be accessible asynchronously, but having the capability to do the "you probably shouldn't" thing (getting/seeing current state synchronously) is, imo, the sort of escape hatch that adds potential & makes greatness possibility. There's a lot of reason we let fear govern language design & direction, but I really believe we also need to give a lot of credit to what we make possible (or not) in these decisions too. For a long time, keeping async & sync separate has been the law of the land. I hope someday we can see values & their changes with less of a everything-neatly-in-one-box-or-another view, & more integratively.
- esprehn 4y agoI don't think that's a sync vs async difference in the API. peek() is related to reading the value without creating a subscription on it: https://preactjs.com/guide/v10/signals#reading-signals-without-subscribing-to-them https://preactjs.com/guide/v10/signals#reading-signals-witho... In general signals looks like a synchronous but lazy system. I don't see anything that's actually async in there?
- rektide 4y agoNot sure how you are getting this conclusion? The example code is as follows: effectCount = effectCount.peek() + 1; That is definitely absolutely clearly 100% for sure sync code. Adding a numerical value +1 to the result of a function (with no wait on it) makes it clear that .peek() here is synchronously returning a value.
- esprehn 4y agoCorrect, .value does exactly the same thing. The only difference is .value subscribes to updates. The framework is all synchronous (but sometimes lazy).
- zebracanevra 4y agoPerhaps a better URL is this blogpost: https://preactjs.com/blog/introducing-signals/ https://preactjs.com/blog/introducing-signals/
- mhagemeister 4y agoPreact Maintainer here. Agree, it explains much better the motivation behind signals. I wish HN would've linked to that instead of the README of the git repository.
- diegof79 4y agoI read the intro and it looks similar to Jotai[1]. But the value getter and the compute tracking looks nicer. Anybody tried it with React pros/cons? [1] https://jotai.org/docs/basics/primitives https://jotai.org/docs/basics/primitives
- Waterluvian 4y agoIs computed just a selector? Is a signal just a useState hook? I’m sure the answers are no. But I’m struggling to see it in the provided examples.
- samtheprogram 4y agoSee: https://preactjs.com/blog/introducing-signals/#signals-to-the-future https://preactjs.com/blog/introducing-signals/#signals-to-th... EDIT: scroll up to "The global state struggle" first to get more background/comparison to useEffect if it isn't clear.
- fabiospampinato 4y agoThese are difficult to understand concepts imo. I have written an annotated implementation of a simple reactive system (you may want to run it through Prettier if that's your thing): https://github.com/fabiospampinato/flimsy/blob/master/src/flimsy.annotated.ts https://github.com/fabiospampinato/flimsy/blob/master/src/fl... A signal is basically a function that you have to go through to read and write a value. In the case of Preact the function is split into getter and setter assigned to the "value" property. The interesting thing about signals is that they can tell their parent computation to re-execute, automatically, without any manual dependency array. A computed is a signal generated from a function rather than a primitive. So like the function that generates the value is re-executed automatically whenever any of the signals read inside it change.
- remram 4y agoBut your description matches useState exactly...
- thelastbender12 4y agoSorry, you seem to hint at knowing this already. But, to clarify, * Hooks - `setState` reruns the full code for your component. * Signals - only the dependent effects/computed/JSX is run.
- nateroling 4y agoMaybe I’m thick, but I don’t see what makes this (or solid, or svelte) different from Knockout or RxJS. Does this somehow avoid the tangled dependency chains and complex push-based reactivity issues that I deal with in RxJS/Angular? To me the brilliance of React is the VDOM, which allows you to treat your components like a render loop in a game. What’s the piece that I’m missing that separates Signals from RxJS observables?
- jonahx 4y agoAfaict this allows you to retain the one-way data model that makes react easy to reason about. This is simply one way to handle your data model (vs "useState", for example). You'd have actions that update the signals (they seem more or less to be streams), and then your components are pure functions of the signal data.
- eyelidlessness 4y agoI think this is very spiritually similar to the intent of Solid, and Solid’s creator often cites Knockout as an inspiration. I haven’t even looked at his Twitter yet since this announcement but I expect he’ll have a lot of nuance to offer on the subject. At a very high level, the thing that this offers versus (anything) is a very minimal reactivity solution that’s small and designed specifically to be integrated into Preact’s VDOM. Other than that, it’s a whole lot of details at a level that most people won’t care about beyond the minutiae of implementation and the nuance people actively implementing these libraries can provide.
- talldan 4y agoI also noticed the similarity to Knockout. The main difference in the API seems to be the use of a value property vs. Knockout's function call to access the value. The computed function seems almost identical.
- jitl 4y agoI read over the React integration here, and it seems like an unsafe approach to React 18+ by monkey-patching React internals state transitions, as well as mutating refs during rendering which can break during concurrent updates. Some abuse of the internals may be "safe" because React Devtools relies on this stuff, but I would be very nervous betting production correctness on this strategy long-term. Maybe it's worth it? Really hard call. Here's the details: https://github.com/preactjs/signals/blob/main/packages/react/src/index.ts https://github.com/preactjs/signals/blob/main/packages/react...
- zerovox 4y agoI had a similar response when reading "React adapter allows you to access signals directly inside your components and will automatically subscribe to them." I don't believe this is worth it. The implementation is fairly dangerous and fragile as you point out, all in order to simplify something fairly clean/reacty such as `const value = useSignalValue(signal)` into `signal.value`.
- iainmerrick 4y agoThe difference (as I understand it) is that you don’t need to list your dependencies as you would with a hook, and you can call it anywhere. I find this approach conceptually simpler than hooks.
- capableweb 4y agoInvisible dependencies and avoid referential transparency sounds more complex to me than the opposite (upfront dependencies and referential transparency), not simpler.
- jitl 4y agoSure, but you could at least wrap the whole component in observer(MyComponent) like Mobx and avoid all this skullduggery with the internals.
- AgentME 4y ago
- cercatrova 4y agoWhy do people like reactivity à la Solid, Vue, MobX etc? The reason I moved away from these towards React was because reactivity introduced subtle bugs with two way data binding, where it was hard to tell where data was being mutated (v-bind, computed() etc). In contrast, with React I don't really have to worry about that because it explicitly mandates one way data binding with only a few places that data can be changed, such as in click handlers. I can basically treat the state as a render loop that runs from top to bottom every time. If I want to preserve state, I use useState, and if I want to cause an effect, I use useEffect.
- fabiospampinato 4y agoOne-way or two-way binding is pretty much completely orthogonal. You can have both approaches in both systems. For instance Solid is pretty close to React in unidirectional flow and read/write segregation. I think the appeal of a reactivity system is different, for some people is better performance, for others it may be better memory usage, for others it may be better DX as a bunch of "workarounds" stop being needed (like dependencies arrays, rules of hooks, useRef, useEvent...), for others it's just simpler to understand and reason about.
- yurishimo 4y agoVue also has a one way data flow, but uses some DX to mask that to something that looks like Angular original two way binding. These days with Vue 3, it’s even more explicitly one way.
- cercatrova 4y agoI think of React as a functional language, not exactly, but as a similar mental model. Once you start thinking of React in such a way, a lot of their decisions make sense. Just as in Haskell we need to use somewhat more convoluted ways to make things work, such as monads, we do the same in React, using useEffect to encapsulate side effects (hence the name). We also keep the tree immutable for much the same reason as in functional languages, because immutability tends towards fewer bugs as we don't mutate internal state, but we optimize such immutability through things like a virtual DOM.
- ZephyrBlu 4y agoIs the main draw of this performance? I gotta say, I think it's a lot more confusing than React hooks. I actually like how useEffect has an explicit dependency array because you know when it will be triggered. Signal effects are implicit, and if you don't want it to trigger you have to use .peek(). I think I prefer React's explicit accessing of previous state to peeking when needed. Why can you destroy effects? That seems like a recipe for disaster. Again, I like how hooks are permanent top-level calls. You know they always run. When are effects executed? React makes it explicit that they are executed after the render. What is the purpose of batching other than performance? Are there any possible negative effects of making multiple signal updates without batching? Writing this has made me a realize I really like the explicitness of React's model.
- fabiospampinato 4y ago> Is the main draw of this performance? I guess performance in some cases, but mainly better developer experience. > I actually like how useEffect has an explicit dependency array because you know when it will be triggered. Signal effects are implicit, and if you don't want it to trigger you have to use .peek(). I think I prefer React's explicit accessing of previous state to peeking when needed. Fair, but in real code, or at least in the code that I write, peeking/untracking is not nearly as common as just reading a signal, on average this should be cleaner. Also with this execution model you can support conditional dependencies, with the dependencies array model the array of dependencies is fixed, so your effect potentially will re-execute even for things that you don't really even read at some point. > What is the purpose of batching other than performance? There's no purpose to batching other than performance. > Are there any possible negative effects of making multiple signal updates without batching? A signal can cause other effects/memos to execute, and they can do arbitrary computations, so the cost of over-executing can be arbitrarily high.
- ZephyrBlu 4y ago> on average this should be cleaner I care more about explicitness than cleanliness though, especially if my effect touches 3+ state variables in a relatively large function. I can definitely believe that the average case is better with signals, but is the average case even important? I don't really care about the average case with something like state management because all the libraries are pretty good for that. It's the edge cases and complex state logic I care more about. Yeah useState, setState, prevState blah blah blah is a lot of overhead for the average case but I really like the explicitness it provides me in the not-so-average cases. What do you mean by conditional dependencies?
- froggertoaster 4y agoWhy would anyone use Preact? If I'm a team lead, why would I choose to use it over plain React? Would not my sense of risk be triggered at least a little? I've talked to many React engineers and none of them have a good reason to use a library like Preact. Once again, the author is content to reinvent the wheel unnecessarily and publish it as something that can/should be used in production. It's a useful toy, a nice exercise in "what can be done" but not much else. Yes, I'm aware of the countless "useless" things that get posted to HN all the time - none of them purport themselves to being anything more than what they are. I have never met anyone with any sense using Preact, and I see no compelling reason to use Signals either when there are countless other libraries doing similar things, with developers that actually use them.
- fabiospampinato 4y agoIs shipping less JS important for your use case? If so Preact can be 10x smaller than React. If only you could do that with all of your dependencies that'd be outstanding. According js-frameworks-benchmark it's also faster at runtime (https://krausest.github.io/js-framework-benchmark/current.html https://krausest.github.io/js-framework-benchmark/current.ht...). If startup and runtime performance have 0 value there's no reason to use Preact. If startup and runtime performance have greater than 0 value there's some reason to consider using Preact.
- acemarke 4y agoFWIW, one specific counter-example would be Etsy choosing to adopt Preact to migrate upwards from React 15: - https://github.com/mq2thez/blog/blob/main/upgrade-react-etsy/preact-vs-react.md https://github.com/mq2thez/blog/blob/main/upgrade-react-etsy... - https://www.etsy.com/codeascraft/mobius-adopting-jsx-while-prioritizing-user-experience/ https://www.etsy.com/codeascraft/mobius-adopting-jsx-while-p...
- devmunchies 4y agoMy top use case for preact was when I needed to embed code into customer’s sites and wanted it to be as lightweight as possible since it was one of my selling points. Think embedded systems but in a web context (like injecting a web form in the middle of an existing page or a modal, completely independent of the rest of the page) Another example, you know those UIs that might drop down when you click on a chrome extension icon? No need to use react for a simple extension drop down. If you’re building a 1st party platform or webapp then react makes more sense. But there’s clearly usecases for React-lite. Regardless, preact core is boring and stable (like 5 years old). I built a multi-million dollar company on it previously for what it’s worth.
- pyentropy 4y agoThe implementation involves unsafe monkey-patching: 1. For React, a hook is injected into every single component (!) regardless of whether it uses signals or not via __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED. 2. For React, React.createElement is patched so that it can render signal values as Text nodes. 3. For Preact, they claim it's using a "pluggable renderer", yet they monkey-patch the global shouldComponentUpdate (and they don't call the old one so they break other patchy libraries) [1] - https://github.com/preactjs/signals/blob/d25e8bac09c94ed3bad7f12fe380becd8bf1a7ad/packages/react/src/index.ts#L131 https://github.com/preactjs/signals/blob/d25e8bac09c94ed3bad... [2] - https://github.com/preactjs/signals/blob/d25e8bac09c94ed3bad7f12fe380becd8bf1a7ad/packages/react/src/index.ts#L27 https://github.com/preactjs/signals/blob/d25e8bac09c94ed3bad... [3] - https://github.com/preactjs/signals/blob/d25e8bac09c94ed3bad7f12fe380becd8bf1a7ad/packages/preact/src/index.ts#L227 https://github.com/preactjs/signals/blob/d25e8bac09c94ed3bad...
- pyentropy 4y agoUpdate: it completely breaks with React 18 and strict mode enabled - https://github.com/preactjs/signals/issues/70 https://github.com/preactjs/signals/issues/70
- bennettdams 4y agoDoes someone know why the counter for this issue is at "70", but the repo only shows "21" issues in total? Maybe there are deleted issues that are not part of the total counter? https://github.com/preactjs/signals/issues?q=is%3Aissue https://github.com/preactjs/signals/issues?q=is%3Aissue
- mhagemeister 4y agoNo, that's because GitHub uses the same number for both issues and pull requests. We did about 70 pull requests. You can verify that yourself by going on an issue and incrementing/decrementing the number until you hit a PR at which point you'll be redirected.
- skybrian 4y ago
- dimgl 4y agoI see lots of negative comments, but this actually looks great to use! I'm all for having more solutions for the global state problem.
- deleted 4y ago[deleted]
- onion2k 4y agoI really wish developers would move away from talking about performance as a benefit of using their libraries. Pretty much all libraries are "fast by default", especially in the trivial examples that library devs tend to publish to show off their work. No matter what library you use, if your app is slow it's probably not the library. When performance is a part of the marketing naive developers move to it expecting their app to magically stop being slow because they used the fast library!. 99.99% of the time the libraries they were using before weren't the bottleneck and moving to a library that's faster doesn't actually solve the performance problems in the application's logic. If the app gets faster it's usually because they removed the slow bit as part of moving to a different library. If you build an application that's genuinely slow because of the library you're using to pass data around, rather than the way you're using that library, I will eat my hat, your hat, and many other hats. In fact, I will adopt an entirely hat-based diet.
- mfbx9da4 4y agoBut some libraries are not build with performance in mind and libraries like [momentjs](https://momentjs.com/ https://momentjs.com/) have proved to be performance bottlenecks on many occasions.
- robertlagrant 4y agoThat's why we have day.js!
- Kiro 4y agoGame UI in the DOM. When you need to update something 60 times a second all performance matters.
- davedx 4y agoI’ve built web games. Don’t bother trying to render the DOM 60 fps, it’s unnecessary. Render your actual game with canvas and use regular react for your UI. If for some reason your UI needs to render that often, move it into canvas too.
- undefinedzero 4y agoSeems very similar to what I have been using for a while now: https://github.com/jorbuedo/react-reactive-var https://github.com/jorbuedo/react-reactive-var. I reckon that library is based on the reactive vars in Apollo client, but without the unnecessary GraphQL code. Reactive vars are great to work with, the implementation is only a few lines of code, it is very predictable, and it doesn't require monkey patching your React internals.
- Tajnymag 4y agoThe syntax looks very similar to Vue 3's composition api. Nothing wrong with that.
- sunesimonsen 4y agoReally interesting to see Preact adopt this kind of model. I have been working on a similar programming model for a while, where this kind of state management is the only approach: https://github.com/sunesimonsen/dependable-view https://github.com/sunesimonsen/dependable-view https://github.com/sunesimonsen/dependable-state https://github.com/sunesimonsen/dependable-state The library has other kinds of agendas like being able to run without a build step, being really small and allow multiple versions in the page. Examples: https://github.com/sunesimonsen/dependable-example-hackernews https://github.com/sunesimonsen/dependable-example-hackernew... https://github.com/sunesimonsen/dependable-example-todomvc https://github.com/sunesimonsen/dependable-example-todomvc
- addicted 4y agoThis is knockout observables reinvented. Stay far far away if you want your app to be maintainable beyond 6 months.
- newbieuser 4y agoI suggest using jotai for atomic state management. What they're trying to do is nice, but I think it's full of unnecessary complexity. https://jotai.org/ https://jotai.org/