Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Thinking Locally with Signals

Posted on Oct 13 As the creator of SolidJS, I was very influenced by React when designing the library. Despite what people might believe by looking at it, it wasn't the technology of Virtual DOM or JSX, but the principles that inspired me. Those technologies may showcase React's capability, and maybe even define it as a solution, but aren't its legacy.Instead, it is things like unidirectional flow, composition, and explicit mutation, that continue to influence how we build user interfaces. As I watch the wave of adoption of Solid's core reactive technology, Signals, by libraries across the whole ecosystem it is vitally important we don't forget the lessons learned from React.The real magic of React is that when you combine all its design principles, the result is that you can reason about how a given component behaves without seeing the rest of the code.This allows new people to be productive without knowing the whole code base. It allows features to be built in isolation without impacting the rest of the application. It lets you return to something that you wrote a year ago and understand what it is doing.These are incredible powers to have for building software. Thankfully aren't limited to the technology choices made by React.Unidirectional Flow is probably the most important part of achieving locality of thinking. Generally, this starts with immutability as with passing something mutable you can never trust it after that call.However, this does not require true immutability to pull off. It does require read/write segregation. Or in other words explicit mutation. The act of passing a value down should not implicitly give the ability to mutate it.So any interface leaving our component whether it be for primitive composition (like custom Signals) or JSX should not by default pass both the value and the setter. We encourage this in SolidJS by using a pass-by-value approach in JSX and by providing primitives that by default are read/write separated.Svelte Runes has taken another way to accomplish this by compiling their variable accesses to Signal reads. A variable can only be passed by value so there is no fear of it being written outside of the current scope.This mechanically is essentially the same Solid example when it gets compiled. In both cases, the Signal doesn't leave the component and the only way to mutate it is defined alongside its definition.This pass-by-value approach is also beneficial for things coming into your component as well. Picture if you could pass Signals or values.We could always check:But picture having to do that everywhere for every prop you use in any component you ever author. SolidJS doesn't even ship with an isSignal to discourage this pattern.As the component author you could force only Signals but that isn't ergonomic. Solid uses functions so maybe not a big deal, but picture if you are using Vue or Preact Signals that use .value. You wouldn't want to force people to:I'm not being critical of those APIs here but emphasizing the importance of maintaining a pass-by-value API surface for props. For ergonomics and performance, you don't want users overwrapping.The way to solve this is to provide the same interface for reactive and non-reactive values. Then mentally treat all props as being reactive if you need them to be. If you treat everything as reactive you don't have to worry about what happens above.In the case of Solid reactive props are getters:Using getters has an added benefit in that writing back to props doesn't update the value, enforcing the mutation control.While it might be the most valuable result of modern UI practices, locality of thinking isn't perfectly achieved in the tools we use today. UI components aren't all pure. They have state. While not necessarily having external side effects the fact that we preserve references in closures that can impact future executions means those executions do matter.Even with following these principles, the one thing we can't control is how often our parent calls us. On one hand, we can think of our components as purely the output of our inputs and this keeps things simple. But sometimes when we hit performance issues it isn't what we are doing but something above and we are forced out of our local frame.Paired with a model that doesn't encourage the parent to re-call us that often does go a long way. This is one of several contributing motivations to why frameworks are choosing Signals over VDOM re-renders. It isn't that Signals can completely avoid over-notification from parents, but that the impact is generally much smaller and it happens less often as the guards are much finer-grained and built into the model.I've talked to many long-time React users who remember when these fine-grained reactive patterns went through their last cycle. They remember crazy cycles of butterfly effects like event notifications. But the reality today is that when we look at Signals those concerns have lost all substance.It is much more like an evolution of the move to break things down into more controllable pieces.Components -> Hooks -> SignalsBut only if we stay to the same principles that were laid out in the first place in React. There is a reason why Solid doesn't have isSignal or Svelte Runes don't allow you to assign a Signal to a variable. We don't want you to worry about the data graph outside of your view.Inside your local scope, there is no way to avoid it. JavaScript doesn't do automated granular updates, so even if we try to hide it with the best compiler imaginable with automated reactivity or memoization you need to have the language to make sense of what you are seeing. The common ground is, that if you treat everything as reactive that could be, the burden of the decision of what is reactive is pushed up to the consumer, regardless of whether you dealing with simple Signals, nested Stores, primitives passed from props or coming from global singletons. Regardless of how heavily you rely on compilation for the solution.The consumer, the owner of the state (or at least the one passing it down), is precisely the one who can make that decision. And if you give them the ability to think locally you unburden them by giving them the confidence that they can make the right one.Templates let you quickly answer FAQs or store snippets for re-use.React has passed many values down over the years. Probably hundreds of trillions. And I hope we learn from all of them.Btw, unidirectionality meant a lot more than just values being passed down. Flux was the unidirectional thing when they first described it, and React was described as a "declarative rendering framework." Declarative rendering with JSX is unidirectional, but it's kind of boring. What's interesting is the full cycle from event to DOM render. That's what MVC vs unidirectionality was about. Without something like Flux, React is an MVC framework. People forget the origins, because most apps require one data source and a few trivial state changes, so it doesn't matter. But React and Flux were designed in an environment that was much more complex than most applications: a chat app. The way most React apps are architected today is not much better than what came before React. It's event handlers controlling various states. JSX and components are the real improvement that stuck. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well Confirm For further actions, you may consider blocking this person and/or reporting abuse Vikas - Oct 12 Vikas - Oct 12 Jack - Oct 13 Dusan Petkovic - Oct 13 Read our welcome letter which is an open invitation for you to join. Once suspended, this-is-learning will not be able to comment or publish posts until their suspension is removed. Once unsuspended, this-is-learning will be able to comment and publish posts again. Once unpublished, all posts by this-is-learning will become hidden and only accessible to themselves. If this-is-learning is not suspended, they can still re-publish their posts from their dashboard. Note: Once unpublished, this post will become invisible to the public and only accessible to Ryan Carniato. They can still re-publish the post if they are not suspended. Thanks for keeping DEV Community safe. Here is what you can do to flag this-is-learning: this-is-learning consistently posts content that violates DEV Community's code of conduct because it is harassing, offensive or spammy. Unflagging this-is-learning will restore default visibility to their posts. DEV Community — A constructive and inclusive social network for software developers. With you every step of your journey. Built on Forem — the open source software that powers DEV and other inclusive communities.Made with love and Ruby on Rails. DEV Community © 2016 - 2023. We're a place where coders share, stay up-to-date and grow their careers.



This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

Thinking Locally with Signals

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×