Video Summary and Transcription
Today's Talk explores the concepts of async local storage and React context. Async local storage is a useful API for retrieving values from a parent component without passing them through multiple components. React context, on the other hand, allows for the creation of context instances in parent components and consumption in child components. The Talk also discusses server actions in React, their limitations, and the use of async local storage in server actions, with an example Cloudflare worker handling web requests and authentication.
1. Introduction to Async Local Storage
Today, I'm going to talk about async local storage and React context. Async local storage, standardized by WinterCG, is a useful API for complex applications that need to retrieve values from a parent component. It allows you to avoid passing values through multiple components. To use async local storage, you import the module from node async hooks, create an instance, and call the run method with a value and a callback function. Then, within the callback function, you can use the get store method to retrieve the value.
Hi, thanks for tuning in. Today I'm going to be talking about async local storage and React context. Async local storage is a somewhat recently standardized API, standardized by WinterCG having come from Node.js. WinterCG is the community group that's responsible for standardizing JavaScript APIs across these new JavaScript run times, things like Cloudflare workers, Deno, Vercel, Netlify, et cetera.
If you haven't seen it before, this is how you get started with async local storage. You first import the module from something called node async hooks. You can create an instance of the storage, often abbreviated down to ALS. You can then call a run method on that storage instance, providing a value and a callback function. And then from within that callback function, you can call this get store method on your store instance. And that will return the value that you passed previously when you called run.
You see, it's pretty straightforward, and it's mostly useful if you have a complex application that you need to get values from like a parent way further earlier in your execution chain, and you don't want to drill it all the way through your app, or if you're handling concurrent JavaScript or something like that.
2. Introduction to React Context
React context has a familiar API. You create an instance of your context in a parent component, provide a value, and then call a child component to consume it. In a modern React app, the client makes a request to the edge, which is turned into a React server components request and sent to regional compute. The returned serialized DOM is translated into HTML through server-side rendering and delivered to the client. The client fetches the JavaScript from the edge, hydrates the HTML into an interactive React app, and can perform server actions that mutate the database or file system.
Similarly, React context has a very familiar API. So, again, you import from the React module. You create an instance of your context. Within a parent component, you can provide a value and then call some child component, and then that child component can use a function to retrieve that value from the parent. Like I say, very similar to ALS, and in fact, there's basically just those three points, creating the instance, a parent that calls it with a value and a child that can consume it.
So, if we just sort of take a step back and remind ourselves of exactly how React app gets rendered these days, because it has changed a bit with React 18 and React components. So, this is just sort of an example of how you might render a modern React app.
The client makes a request to the edge. This then gets turned into an RSC, React server components request, and gets sent to some sort of regional compute. This is near to where your database or upstream or whatever it is happens to be. This then does some serious server things. This is maybe speaking to a file system or, like I say, a database or a SQLite thing or whatever it is. And this all happens far away from user in regional compute. The RC then is translated into some sort of like serialized DOM. It's a very interesting format. Basically it allows for streaming, that sort of thing. This goes back to the edge. The edge then turns this into HTML through server side rendering, and sends it back to the client.
The client has then got a HTML page, but they need to actually fetch the JavaScript to make it interactive. So, they request that from the edge. And because it's the edge, it just delivers the asset straight back to the client fast and speedy. Client then does client side hydration. That's actually turning the HTML that it has on the page at the moment into an actual interactive React app. And then we might actually want to take action. So, I might click submit on a form. And that's going to send a server action to over the network sending my form values. It's gonna be proxied through the edge and end up in the region. And again, that's going to be able to do some serious server things. That might be mutating my database or hitting the file system. Whatever.
3. Exploring Server Actions and Tradeoffs
Server actions in React only run on the server and are not available on the client. React context is not accessible within server actions. React context is limited to the client and the edge, while async local storage is a server-only API. Async local storage serves as a bridge between the platform and the React app.
So, if you haven't seen it before, this is what server actions look like. We have a bit of pragma at the top saying, hey, this only runs on the server. It's not available on the client. You can see I'm exporting a function. It only takes one parameter. It's that form of data that we originally submitted. And you see the return value is not JSX. It can be any JavaScript object.
This is interesting. We already know that this is not really running like React normally does. In fact, we don't have any tree here. We're not rendering JSX. There is no context to like no parent component or anything like that. So, React context actually isn't available to us within a server action.
So, if we look at our network diagram again, what are the capabilities of each of these pieces? Well, like I say, region is only it's not able to deal with React context because it doesn't have any of that information. It's only got the server actions piece. So, React context is relegated to just the client and the edge. But on the other side, async local storage is a server only API. It isn't available on the client. So, we have this tradeoff. That's what this talk is about. When to use React context. When to use async local storage. And I think that async local storage is most useful as a handoff between the platform and your React app. So, rather than trying to intermingle them all and constantly just flip between the two, it's better to just treat async local storage as that sort of bridge between your platform, your host, and the React app.
4. Using Async Local Storage in Server Actions
This example Cloudflare worker handles web requests and authentication. The async local storage instance is called with useful values to handle server actions. It can access the incoming request, check headers, and use the env for database access. The Aviation context utility, which uses async local storage, serves as a helper for concurrent JavaScript request-based tasks.
So, this is an example Cloudflare worker. We see we're doing export default fetch. That's basically saying, hey, we're gonna handle web requests. And you're getting the incoming web requests. You're also getting the env. This is where all the bindings are available. Any databases or anything like that. Any environment variables, secrets, that sort of thing.
We are also performing some sort of authentication here. We're checking that the user is allowed to actually access our app. And we're getting back information about who that user is. But they're going to call our async local storage instance, its run method, with all three of those values. Because they are useful in some way to my React app. It wants to then use them in handling server actions or whatever.
And so, handle React app here is a fictional method. But that is either doing the SSR at the edge or it's handling the server actions in the regional compute. And so, to see what that sort of looks like in the server actions, again, we have just our fictional server action here. It's importing that storage instance from our entry point and calling get store. And it's able to use those values. It's getting information about the incoming request. Might be able to check additional headers or whatever. With env, I could access my databases. I could check to see that that incoming form request with its user and password or whatever, is valid. And the user itself is available, if I've already authenticated or whatever.
So, I really got started with async local storage by sort of building out a new React meta framework. It's called Aviation. And as part of that, I've published an Aviation context utility. This is available on NPM. You can install it now. It uses async local storage and it's mostly just a helper utility for any sort of concurrent JavaScript request based stuff. So, take a look if you want to learn some more about how async local storage can be used as a platform handoff. So, again, I started this talk saying it's async local storage versus React context. But I hope that you now realize that in fact, async local storage is really the bridge between platform and React apps. Thank you very much for listening.
Comments