Hardly a week goes by without somebody talking to me or talking to the comment stream about signals.
And it's almost always about React.
When will react get signals react sucks because it doesn't have signals should I use signals in my production react application and by the way
My answer today is no no way if you want to use signals in production
There are lots of great options
There's solid quick angular and coming up spelt and view and more
React doesn't support signals and the way that folks have tried to do so far is in pretty janky
So my answer now is no for right now And then answer isn't going to change after this video,
even though this video is about how the landscape of signals has fundamentally changed because now there is a proposal to bake signals into the browser.
The promise is that the frameworks that we love today would adopt those standard signals as a consistent way to model application state and potentially frameworks could even interoperate between each other by sharing state through.
So there's a link in the description right down below to the proposal,
as well as another good article that does some analysis and has some code that I stole for this video.
But the proposal has a polyfield library, and I couldn't help myself.
So I figured out a reasonably decent way of interfacing we propose standards based signals with React and another framework.
Be sure to stick around for the end of the video for that.
You're not going to want to miss it.
But in the meantime, let's jump in to trying out these new signals with React.
This is not something you want to put into prod today.
The proposal is still very early days.
The interfacing code I'm about to show you is probably pretty janky.
This is a video about what's coming next in JS and not what's for today we good.
Okay, let's go All right, this is one half of the application that we're gonna build
It's got a shopping cart on there with apples bananas and cherries in it and we can add cherries.
And our total changes, we apply a discount, and then we get a grand total update.
We're going to use Astro to build out this application because Astro allows us to have islands of interactivity.
This react shopping list is such an island.
And we get to try out different frameworks in the same.
application, which is going to come up handy as we add the second part of this application.
In the meantime, let's go build out our Astro app.
I'm going to create Astro latest to create signals shopping cart.
I'm going to off with an empty template.
I do plan to use TypeScript to I'm going to use it pretty relaxed.
We're going to use just a few generics.
I am going to install the dependencies and I'm going to initialize a new git repo.
Of course, all the code that you're going to see today is linked in the description right down below.
Let's bring that up in VS code.
First thing I want to do is add tail wind.
That's what I'm going to for this.
So to do that, I'm going to use the astro ad command and give it tailwind.
That's the integration for tailwind.
It automatically sets up tailwind and integrates it into our astro app.
Astro integrations are awesome.
Yes, we'll add astro and we'll get a tailwind config file.
These are the changes that are to make to our astro config and that's it.
Next, we want to try a react.
So we need to integrate react into our app.
To do that again, we use AstroAdd and React.
Now it's adding React to our list of integrations, setting some TypeScript options, and that's it.
And the next thing we're going do is we're going to model our data.
So let's go take a look at our application and see what that data is.
So the data we have is a cart that's going to be the items in the cart.
We're also going to have a total that'll be computed from all of the items in the cart.
That's also a mutable piece of state.
You can change the discount and then we have the grand total, which takes the total applies that discount and there's your grand total.
So let's go model that in signals.
Now to start modeling in signals, I need to bring in the signals polyfill.
And then I'll go and create a new file in source called global signals,
because we're going to find some signals and they're going to be global.
I'll import the signal class from signal polyfill.
You instantiate signal dot state to create a state based signal.
Let's try that with our cart.
So we'll have an exported constant.
that just instantiates the new signal.state,
so it's a state signal, so it can be get and also set, so it's mutable, and then we give it the initial value.
So in case, that would be apple banana and cherry, a certain number of count and a certain number of price.
Now we wanna compute total from that cart.
So to do that, we instantiate a new signal.computed.
We tell what we're going to create, which is a number, and then we use cart.get to listen to the cart.
And the cart changes, we run .reduce on that array, and we accumulate the price times the count.
That gives us our total next up.
We want to create some mutable state for the discount.
What should we use for that?
So gonna use signal dot state again because we want to be able to mutate that state and we'll give it an initial value of 0.1
Pretty nifty discount and now we want to compute the grand total based on the total with the discount
So what are we going to use that?
Well, we're to use And again, we use that get function to get total as well as discount and multiply those together dot get inside of the computed is essentially how you subscribe to that signal.
Now, if this seems vaguely reminiscent of recoil or Jotai atoms, you're not far off.
If you're looking for a way to do a signal style data structure in react today, Jotyrequa might be a good way to go.
So now that we have our data, we want to wait for our code to listen to it.
Now, if you look at the spec, they talk a lot about an effect function, except that there's no effect function in the library.
What we have to do is create an effect function, which allows us to listen to a signal in our framework.
So let's go and create an effect function to do that.
I'll create a file called effect.ts.
And again, I'll bring in our polyfill.
Next time I create a signal watcher.
Now signal that watcher in the subtle name space watches signal.
Why is it in the subtle name space?
Well, it's subtle because how you integrate signals into your framework, again, probably done by a framework author, not folks like us.
is subtle, therefore it is in the subtle namespace.
But we're gonna take a kind of generic approach.
So we need to create this process pending, which is called whenever a signal changes.
Our process pending is going to go through any pending state updates provided by that watcher, and then pull the data from the signal.
It is a pull-based mechanism,
as opposed to you pushing out a change in signals land, and the efficient way to go is through pull.
And then we'll take Our effect function will take a callback function.
That callback function is going to be our integration with our signals.
So going to say, OK, we're going to get data from the signal and by getting it, we'll essentially subscribe to it.
So in here, we are creating a new computed signal.
That's going to be what the watcher is going to listen to.
we're going to our callback,
which is, again, going to call more signals, again, doing more subscriptions, and then we'll kick off that watch and get the first value.
And we'll return a that essentially unwatches or unsubscribes from those signals.
And that's part two of a four part connection to react.
So let's create the custom hooks that are going to take our effect function and bring it into our react state ecosystem.
So I'm going to a new directory called react components.
And in there, I'm going to create a new hook called uSignal.
And I'm going to bring in one hook from react use sync external store using external store is a very obscure library hook that is meant to connect reality
And it's state ecosystem with external stores.
Think Redux, Sushtand, Jotai, any of those.
So you've got data somewhere else that you want to connect into React
and use sync external store is the mechanism that React has provided to do that connection.
So the first talk that we're going to create is use computed, since it's the simplest one.
This is a custom book that's going to allow us to get the value of a signal.
So let's bring it that gives us a type.
So we'll turn that into a generic that will then export as the return value of this function.
That keeps it end to end type safe.
And then we're going to use that using external store.
Now, our first function in there is going to call effects.
So let's bring Looks like we're now TypeScript error-free.
Let's talk about how you sync external store works.
Using external store takes three arguments.
We'll get to that in a second.
The second and third are snapshot getters.
The first is the client-side snapshot getter.
The second is the server-side snapshot getter.
Aster does run in a server-side mode, so we need to provide that.
Next up is that first callback.
that is react giving you a callback function that then you call whenever your external store changes.
So to do that, we're going to use that effect function.
we are going to subscribe that signal by doing signal dot get,
we're going to have that get the value,
which is then I'm going to off locally in this closure and then whenever this changes that's when effect
gets called we're just going to call that callback until react that it has
changed and then react is going to call the snapshot function to get the new value.
Now that we know how that works use signal which is the mutable version UseSignal takes a mutable signal and returns a tuple.
That tuple has the value, the current value first, and then second, a setter function.
If that sounds from A2, that's because it's exactly the same API service as useState, except that the function doesn't take another function, but whatever.
So how we're going to do that?
We're going to use exactly the same use external store that we did with useConnect.
but we're also going to provide a secondary function that just sets that signal when you call it.
Those are the two functions we need to connect our React code with our signals.
Of course, we don't have any React code, so let's go and build that.
So I'm going to a new component in here called React Shopping Cart.
And into there, I'll bring our fresh new use computed and use signal hooks that we just created.
I'll also bring in our signals, cart, discount, total, and grand total because we'll need all of those for our interface.
Next up, we're gonna build the cart list component.
That's this entire section,
including React Shopping List and the whole list of all the items in the cart and the total and the discount on all of that.
So let's go and build out that component.
So we're going to start off by using use signal and use computed where appropriate.
So when it's a mutable signal, we'll use use signal to go get that data as well as any setters.
We're not actually going to use the setters in this case.
And then for the computed signals, we'll use use computed.
After that, it just basically comes down to a bunch of JSX formatting.
all of this code is able to you for free in GitHub and the link in the description right down below,
but this is standard react stuff.
putting header block on the top, iterating through all the card items, and then putting out the total amount and all of that.
All right, let's slide down here towards the end.
and then create our React Shopping List component and export it as the default.
So for the moment, we're just going to have the cart list in there.
Now let's go bring that into our Astro,
and then we're going to replace our Bonnie with a nice dark mode version and a Flexbox that gives us a two column layout.
Let's hit Save and see how we look.
All right, looking pretty good so far.
Let's go and change the discount and see if it updates.
We'll make it a 30% discount, hit save, and there we go.
We got a discount now of 30%, so it looks like we are listening to those signals.
Take that back down to 10%, we're not giving away our produce.
So now we want to wait and mutate that store,
so let's go back and react to shopping cart and create a button that we can use to add items to our shopping cart.
We'll call that add product button,
it'll take a name and a price for our product and then we'll put up a button nicely formatted that just sets the cart items.
We take the existing cart items and then we add our item to it and we create a new array.
As with every other state mechanism in React,
all of this works off of referential identity,
so in order to update the card items,
you need to give a new array reference because it's the reference that matters, not the contents of the array that matters.
Now let's hit save, and then we'll go down here and add some buttons.
once it's saved, try it out, and there we go.
Okay, so let's take a look at where we are so far.
So we have defined some signals, the base state of the application, that's section one.
One, we created an effect function that's
going to get called back whenever any of the signals that we call when we are in that function change.
We then connect that effect function to react using use signal and use computed, which use sync external store to give us a callback.
We then wrap that in an effect and call back whenever those signals change.
And then finally, we connect that to our components using those custom hooks inside of our React components.
Now, if you're familiar with other signal frameworks like Preact Signals or Legend Signals,
those connect directly to the DOM in addition to providing this signal's architecture, that's not happening here.
This is using a very traditional React approach any time any of those signals are happening
the whole component that listens to that signal will re-render.
So I would consider this a much safer approach, although again, not in prod.
Now, as I mentioned, you can do this in other frameworks as well.
And of course, we're an Astro, which means we can very easily bring in other frameworks.
So who is our mystery framework guest?
Svelte, we're going to go and connect Svelte to our application.
So now they'll both be working off the exact same code base.
If I hit more discount here, we can see that the discount on the react side goes up.
and everything works because they're both talking to exactly the same state source.
We on the same page a React application, a Svelte application, and they are both talking through the same standard signals.
Well let's go and add our Svelte component.
To do that, we are going to start off by adding the Svelte integration.
We simply do Asarad Svelte.
Now we've got Svelte going.
So let's make ourselves a very simple Svelte component.
I'll create another directory called Svelte components.
And we'll call this one disk counter.
We'll give it the Svelte extension.
If you're not familiar with Svelte, all the files are named .Svelte that are Svelte's components.
And we're going to import the mutable discount signal and then format it in our component.
So all we're going to really do is discount.get and multiply by 100 and then take off any trailing zeros.
So now let's go and bring that in our page.
To do that, we'll first import it and then down here, we'll add it as the second column, take a look, and there we go.
Now we've got our discount going over there.
Of course, it's not mutable yet.
So let's go and add some buttons to increase and decrease the discount.
To do that, we're going to bring in two functions, more discount and less discount.
And all we're gonna do is use the native signal functions
to set the value after getting the value and adding on either plus one or minus one.
Now this dot get dot set stuff is fairly contentious.
If you look at the proposal,
some folks are saying in response to it, that they would much rather have a proxy based interface or something along that line.
I personally don't mind the dot set and dot get.
But if you feel like that's an important thing here, then you should go and comment on the RFC.
All right, let's add some buttons for more and less discount.
And now we can add more, okay.
I can see the discount is going up over on the react side less, and there you go, that looks good.
But we're not actually updating the discount on the spelt side.
Let's bring in that effect function.
Then let's create a local for discount value.
And then we will use that in place of discount.get.
Let's initialize that actually with.get.
And then we'll use that effects function to set discount value whenever the discount changes.
Let's save and there we go looking good,
and you thought I was going to end it there surprise No,
I'm not actually I was going to but now I'm not because Jut and Ramanathan who is one of the authors of the spec had a look at this video before you got to see it
and said you know what you really do need to cover direct dumb manipulation with signals
So who am I to question that let's try it out So I go back into our components,
I'm looking at the React Shopping Cartlist,
I'm just going to add another div down here,
we'll put in their discount,
and inside that we'll put a span with the discount value currently, and we're going to set that to a string.
Just to make sure that if you use this with an object or an array, you don't blow up anything.
Let's take a look and see what happens there.
But of course, if we add or remove anything from that discount, we don't get any changes.
So what we can do is we can go and bring in userf from react.
Now down here, we'll add a ref for our discount.
We'll set it to that span.
And then in our effect, we'll set the current text to that discount.
We'll make sure that it exists.
We'll add a typing to the user app to make it happy.
And then we'll do another conversion to string.
This mostly about TypeScript at this point.
All right, let's hit Save and go.
And now, as we add more discount and less discount, we can see it directly updating that element in place.
If we go over here to our dev tools and then go into React components,
all we really need to do is just hang out there because as we make changes,
we can see everything updating, but we can But as you see, The discount is not actually updating.
The updating is signaled by the bracketing around the component.
Only the react shopping list is actually doing a re-render at this point.
The discount is simply updating in place.
Now I really should go and wrap this effect in a use effect.
Now, I'm just going to cut this effect and put it in there as what we're going to call.
The reason for that is that effect returns an unsubscribe, which is what use effect wants, so that's good.
I'll add an empty dependency array, and looks like we're safe.
Fantastic, awesome, reasonably safe react signal updates without actually doing any re-renders.
So thank you for the feedback, John, and now back to our regularly scheduled conclusions.
All right, as I mentioned at the outset of this video, this is what's coming next in JavaScript.
It's not all we have today, so I would definitely would not put this into production.
But it is fascinating to see.
We two different frameworks collaborating on the same page, over the same data.
I that the Signals API is fine.
I like that Jotai slash recoil API style.
There is a first look at signals both in React and in SELP in their native form.
If you like content like this, React, Next.js.
advanced topics, be sure to go to pronextjs.dev.
That's where I'm building my course on next JS.
It is in depth and I'm really excited about getting it out to you.
In the meantime, of course, there are free state management tutorials and forms management tutorials.
If you sign up for the mailing list, I'll see you there.