 It's going to be good. It's going to be great. It's going to be great. Yeah, bestie, you told me to be more euphoric. Come on, let's get the energy out. Well, this is not a broadcast. This is, you know. OK, here comes clappy time. Three, two, one. So, OK. So. Lists of things are pure clickbait, right? It works, doesn't it, when you say like your. Oh, you mean like the 10 things that you can do to be rich? Number four, well, blow your mind. Yeah, and like that stuff, I know it's clickbait, but it still works on me. I just can't stop myself. So I thought, I want to get in on that tasty, tasty click action. So this is 3.143 ways to synchronize across documents. I'm actually going to talk about six things, but. This is annoyingly far off of pie. Like, couldn't you have made it 3.141? Yeah, well, I don't know. No, I decided not to. Do you know what, I noticed the exact same thing as well. And that did bug me. And I thought. But also I like that you never use this font. You use, this is your clickbait font, I presume. Yeah, yeah. And it's the only time I'm going to be using in this presentation as well. So enjoy it. Good. Enjoy it right now. Yeah, I use some decimals because I'm not, all the things that I'm going to show, I'm not sure all of them count as one complete way. So I've, I've docked a few points here and there. So, OK. Well, when doing clickbait, at least do it fairly. Exactly. Yeah, I do have some principles, some. So be prepared for a shock. Did you know that you can have multiple tabs open to a page? Dun, dun, dun. I know. It's actually really annoying because whenever you write your service worker update code, that's the case that makes everything a lot harder. Yes. Well, not just that, it's also the overlap between one page refreshing. There's a kind of time when both pages are somewhat alive. Or certainly the request for the new page is going out while you're paid. Anyway, topic for another day. The thing I want to talk about is page state because, well, some state is in your page, but some application level state is across those tabs. Like login state, right? You log in and one tab, you would now be expected to be logged in and another tab. But quite frequently, a lot of sites don't do anything about this. No, I think barely any site. I don't think Google sites do that. Well, that's shame on them, I say. And this is worse for single page apps because in a multi-page app, your next navigation is going to pick it up, right? So. Yeah. But a single page app, it might be around a lot longer. So these differences in state are going to become more noticeable the longer two tabs are alive. There's also client-side state stuff. Like if you've got a dark mode toggle on your site, like is that per page or is that for your application? There's this. So you remember this when we built the demo website? We wrote this. I was really proud of this because this is the one thing I really wanted from any other global event. And so it's a time zone toggle, like tell me the times where I am or tell me the times where the event is. Because if you're going to the event, you want to know the San Francisco time. When do I have to get up? Exactly. And if you're watching at home, again, when do I have to get up? It's probably going to be a less sociable time. How long do I have to not go to sleep? Yes, that was mainly the difference. So yeah, in one tab, change the time zone because this is going to be used in multiple pages. You want it to be updated everywhere. So how do we do this kind of stuff? Did we do that? Yeah, yeah, I did. And I'll talk about the way I did it as well. Let's go through a number of different ways of doing stuff like this. Here's number one. There you go. Post message. Job done. That's one way of doing it. How do you know your other window? I guess the service one kind of is all the windows. That's correct. You don't know the other windows because the only way you'll get one of these is if you do window.open or if it's an iframe. So I'm going to have to dock a load of points for that. I'm afraid this is not a proper method, is it? It's an eighth of a method, apparently. Yeah, well, yeah. But also, it has some good parts. You can, the stuff you can transfer, it could be not just string data, not even just JSON data. It can be binary data, blobs. You can have circular references and your objects is a structure cloneable. So it supports all of that. Have you heard of comlink? Yes, and I'm sure comlink can help here as well. And I should say that this is the only method we're going to look at here that actually works cross-origin. So I'm actually going to give it some points back for that. So it has sort of specific use cases, but not, like you said, not generally useful like this. It's good at what it does specifically, but for keeping state across the whole app, it's just 0.248 other way. I would say I don't have time to go through the very complicated and scientific scoring system I'm going to be using here, but just trust me that it is... Just trust you that this is absolutely, deeply thought out and completely accurate. Absolutely, absolutely. If I did the same test again, the same numbers would come out. I've not just picked these out of my... How dare you? How very dare you even suggest... You picked them out of your what, Jake? You picked them. All right, let's leave that behind. Next one. This is a vent source. So this is a persistent server connection. There's other ways of doing this. You could use Vetch. Vetch streams, have you heard of them? But that doesn't work in Edge, annoyingly. Well, old Edge, sorry, the old Edge engine. And there's WebSockets as well. And you need a server. That's, I mean, come on, this needs a server. And this needs a server. So, yeah, that's... Don't like it. Don't like it. Well, and do you know what you're right? Because if you've got eight tabs open, that's now eight connections you're keeping. And the same data being sent to you eight times. So I'm sorry, gonna have to dock some points for that. That's no good, is it? But it does mean you can communicate cross-device, which is pretty good. How do you know that the event sources are coming from the same client on the server side, reliably? Well, cookies. So using cookies you have state, you'll know the login. Fine. Yeah, I mean, sometimes you don't need that. I don't know, I find it really satisfying if I close an issue or merge an issue on GitHub, on my phone, and I see it happen on the screen as well. It's like, ooh, that's good. But that's public. Okay, I was still thinking in like the time zone thing, which is an inherently personal adjustment while something like an issue that's, quote unquote global state, so that makes sense. Yeah, okay, I'm with you. Yeah, so I'm actually gonna give it a few points back because it's useful for, you can do some smart cross-device stuff, but yeah, it's very specific. And yeah, it's a shame that we don't really have a way right now to sensibly de-jupe, like a connection. So if you're trying to do that, you've got eight tabs open, you kind of want one to be the main tab and then do a different method of communication to give that information to the other tabs. SharedWorker is a great way of doing this, but it's not well supported. I was waiting if I should, if I can bring up SharedWorker if you have it in here because that is the thing that I wanted to use for my actor model stuff as well, but it's so horribly, badly supported that it's just not a realistic option. I literally just thought of another thing you can use for this. The WebLocks API, you could have one thing set up a connection and create a WebLock and then the other tabs will wait on that WebLock to expire before they try and do it. I don't know, there's probably some race conditions in there. Anyway, anyway, I haven't thought about it properly, so I'm not gonna talk about it anymore for fear of embarrassing myself. Here's another one. This is like, when you change local storage or session storage, all the other windows, they get this event. I did not know this. I think this is really cool because you get the old valve, the new valve. You even get the URL of the page which made the change. Can local storage store objects? No, it can't. Just text data, so I'm gonna dock some points for that reason. But it's fine for small bits of data. You could also use it, I've done this before, use it as a signal just to say this thing's changed, even though the data is actually somewhere else, like indexedDB or cache API, I can use this a little. Yeah, I think this would have been useful for my actor model thing because what we ended up doing is using IDB and polling it, it's the word, polling, polling. Yeah, still, that's harsh, yeah. So one reason you might not have used it in your actor model is it only works in a window, not a worker, because local storage, right? So. Yeah, that would make it a non-starter. Yeah, exactly. So let's dock some more points for that. Although that hasn't been a problem in practice for me when I've used this in the past, this is actually how I did the time zone change because it was only ever reflected in a page. That makes all of sense to me. So I used it, yeah, and it was dead simple. If you're only dealing with dedicated workers, you could have the page here, this event, and then post-message the worker, whatever. True. But my main problem with this is it only alerts other windows to the changes, which is a little bit of a pain. So in this case, I've got another component on my page and it's listening for this event as well. But if the other component updates a value, my component's not gonna hear about it because it's only gonna tell other windows. Oh, it doesn't. You don't get an event on your own window. Okay. Exactly. Yeah, that's annoying. That is annoying. Yes, that's a problem. So yeah, I'm gonna dock some more points. I mean, it's still really useful, but I'm gonna say this is like 0.763 of a method due to, according to my calculations. Okay, let's keep going. Broadcast channel. Ah, love it. Love it. If it was real. Yes, it is real. This is real. You can use this today. Barely. It's, but one of the nice things about this is like, it's structured cloning, which is great. Works in workers, which is great. So, you know, all different kinds of objects and whatever. But also it will message other channels on the same page. It won't message itself, but it will message any other channels of the same names. You don't have that problem that I showed before. It just works around that, which is really nice. But, and I think you were slightly alluding to this. It's not supported in Safari, is it? It's not. And that's the only modern browser that doesn't support it. Chrome was late to the party with this. Like it was in Firefox for a while before it reached Chrome. But we've had it for a few years now. And this is like top of my list of things I really want in Safari. Yeah, anyway, anyway. I'm not over it, but we'll move past it for now. Service worker. So, this listens for events that are sent by the service worker to other clients. So, this works in workers, it's structured cloning again. A little bit more complicated because you have to do things manually when it comes to sending, so. Yeah, the service worker side is a bit convoluted as well. Yeah, so here I am post messaging to the service worker to say, hey, please broadcast this for me. And then the service worker is going to pick up that event and then broadcast it back. This is useful in the multi-component case as well because you get to decide whether it posts back to the same client. And I tend to, I always post back to the main client. In fact, I use that as my source of truth. Like one component will send the data to the service worker, but then it won't apply that data change until it hears back about it. And that works in a multi-component, like web components case, so. But that's preference, you get to do whatever you want. So that's one way of doing it. And I'm actually gonna leave that at one because that's my favorite method when I need all of those features. And the post message, sending something to the service worker will wake it up in case it's been killed and it will have always a correct and complete list of all the clients, right? Yes, yeah. When you call match clients, that's when it will go and get all of those. By default, it's only controlled clients, but there's an option to say, give me the uncontrolled ones as well. By default, it's only pages, but there's another option where you say, I know I want the workers as well. So that's kind of, yeah. That's all in your control and up to you. One last one. Here we go. Oh, look at this. This is, are they finally a thing? An IDB observer. Yes. So you create a callback and then you say what you want to observe. Works and workers. You get the complex type because it's all of the IDB stuff. It has everything. So you can just use your index TV as like the central store for your state and then just your components can listen for when the bit they care about actually changes. So it's a way for... And it's not supported. It's just an explainer. It's, I think there's a partial experimental implementation in Chrome, but like, and worse, this explainer hasn't been touched in 2017. Oh. There's a lot of years in web years. You got me hopeful there for a second. I know. I knew they were working on it and I was like, is it happening? Are they a thing? I just wanted to play with your emotions a bit there because it's better to say that we both really want this, right? This is how we want to build apps. We want to be able to store all the data in index TV and then use observers to, you know, so one component is like, I only care about this bit. I only care when this thing changes. So just let me, let me know. Yeah, imagine if you could put your redux state into IDB and have all the other components just subscribed to it via IDB observer. And if the page reloads, it doesn't matter, all the state is still there. It's like, it would be so many things I wanted to try if they are actually feasible. Actually, I would love to also have a session index TV store as well. So a way that you would say like, I want this, but just for this tab, and if this tab closes, I can get rid of the data. And that also means like, depends again, depends who you ask whether this is the spec behavior or not, session storage in some cases survives a tab crash. So tab crashes, reload, and you know, you've only lost whatever happened before your last commit to IDB, which is great as well, right? So yeah, I know I'm not, I'm ending on a sad, sad story. On a point zero, zero, five. That's all I've got. Hopefully one day we get this, but in the meantime, yeah, I think the service won't go away. Now I'm thinking, do your numbers actually add up to 3.145? I hope so. 3.143? I hope so, because I sort of panic added it up at the end, but it is entirely possible. I miss it. But you did at least add them up. If it was me, I probably would have just written math.random on every slide here. I definitely attempted to add them up, whether I got it right or not. Do you know what? I reckon we'll hear about that in the comments. Oh yeah, we will. And you will get told off if it's not correct. It's all I deserve. Just one second, because I'm really worried because my fan went off during that episode. So I just want to double check that everything actually recorded. I'm not, I don't have any error messages. And I have 17 minutes of recording. Yeah, yeah, yeah, yeah, it's all recorded. Thank Christ for that.