Secret React Server Component Patterns They Don't Want You To Know

Secret React Server Component Patterns They Don't Want You To Know


Secret React Server Component Patterns They Don't Want You To Know

NextJS 13’s RSCs are just one vision of how we could use RSCs. The underlying technology gives us a lot more options and Dai-shi Kato has explored those in his new framework Wakuwork, including one that, well, framework authors don’t want you to know about, but you may love as it changes the relationship between client and server completely.

Code: https://github.com/jherr/better-rscs
Wakuwork examples: https://github.com/dai-shi/wakuwork/t

👉 I’m a host on the React Round-Up podcast: https://devchat.tv/podcasts/react-rou
👉 Don’t forget to subscribe to this channel for more updates: https://bit.ly/2E7drfJ
👉 Discord server signup: https://discord.gg/ddMZFtTDa5
👉 VS Code theme and font? Night Wolf [black] and Operator Mono
👉 Terminal Theme and font? oh-my-posh with powerlevel10k_rainbow and SpaceMono NF

0:00 Intro
1:32 Example App Introduction
2:56 NextJS 13 Implementation
10:06 Wakuwork React Server Functions
16:06 Wakuwork Nested RSCs
20:36 The “Waterfall” Problem
22:09 Outro


Content

0.133 -> In a recent poll on Twitter,
1.2 -> 66% of you said that when you heard React Server Components for the first time,
5 -> what you heard was a component that worked just like a client component.
9 -> But where you when you change the properties,
11.533 -> it would go off to the server asynchronously, create a new component
15.766 -> for you, and then slotted in to where that component was in your tree.
20.733 -> It was a component that was just rendered on the server.
25.166 -> And what we got with next NextJS 13 was not that.
29.3 -> But it turns out that the RSC support that's built in to React actually supports
34.766 -> multiple models of React Server Components or RSCs,
38.933 -> and we're only seeing probably the most conservative one in NextJS 13.
44.4 -> What Remix is going to do with RSCs is is still up in the air.
47.2 -> And there's this really cool new framework from Dai-shi Kato, the guy that gave us
52.3 -> Jotai and Zustand called Wakuwork.
56.533 -> And it actually shows some very different models
59.3 -> of using RSC that provide a much better developer experience.
64.166 -> Now of course, none of this is ready for primetime, including NextJS
68.833 -> 13’s app router directory, which is still in beta,
72.333 -> but I think there's time to make changes or add on additional RSC
77.633 -> functionality to NextJS 13, so we can have it there as well.
80.933 -> Anyway, enough talking.
81.766 -> If you're into React architecture stuff, you're going to love this video.
84.266 -> Stick around. Let's get into it.
86.933 -> This is the example application that we're going to build three
93 -> different ways once using NextJS 13
96.1 -> and then two times using Wakuwork.
99.4 -> It's a very simple e-commerce system.
101.4 -> It has a jacket selector color here and then you get some additional things
105.933 -> like, for example, if you like, a blue jacket, you might like a blue collar
109.2 -> or a blue harness or a blue leash, and you can add those items to the cart.
114.433 -> Now, besides the cute dogs, the most important thing visually about this
117.733 -> example are the border boxes around the different components.
121.2 -> A red border box indicates that this is a server component,
125.1 -> and a green border box represents that.
128.033 -> This is a client component.
130.133 -> And this example that we're looking at right here
133.633 -> does the unfathomable, the unthinkable,
137.266 -> the terrible component weaving, which means that it’s got as
142.8 -> the outer boundary, a React Server Component
145.5 -> and then nested within that a client component
148.466 -> which is then nesting a React Server Component again, A no no.
153.2 -> And that React Server Component does a triple
155.4 -> no no of bringing in client side components.
158.766 -> So this is apparently a terrible example and yet it actually still works.
164.333 -> And this is the last example that we are going to do.
167.266 -> So stick around
168.066 -> and you'll see how this is done and it's actually really, really cool.
171.166 -> And then I'll talk about why pretty much every framework
173.666 -> author will tell you that this is a terrible idea.
176.5 -> The first thing we're going to do is build out the NextJS 13 version of this.
179.3 -> And the idea is to give you a reference point,
182.3 -> something you understand and you're familiar with.
184.566 -> So as we look at what could work and compare the different
187.4 -> solutions together, you'll have a good reference point here.
190.566 -> We have the Project in VS code.
192.233 -> All of this code is available to you on GitHub for free.
194.7 -> As always, and a link in the description below.
197.333 -> And the first we’re going to take a look at is the Next JS 13 version,
200.866 -> so I'm going to bring that up right now by doing pnpm dev.
205.333 -> And what we can see in this version is that
206.933 -> we don't have that carousel at the bottom, and that's the thing
210.2 -> that we're really going to focus on in this example is the implementation
213.466 -> of the recommender carousel, because we want to be able
215.766 -> to have some different options on implementing that.
218.433 -> Now in the NextJS version, that's going to have to be a client side
221.266 -> component and it's going to have to make a call out to an API to get that data.
226.2 -> So the first thing we need to do is establish that API of recommendations.
230.2 -> All right.
230.433 -> In NextJS 13 in the src directory under
234.3 -> app, we're going to go and add a new directory called combinations.
239.766 -> And then within that we're
240.6 -> going to create another folder called [color]
243.8 -> within brackets, and that's going to take the color choice
247.466 -> and give us back the JSON payload that has the products that are appropriate
251.866 -> for that color of jacket.
254.766 -> So a blue jacket is going to give us a blue leash,
256.8 -> a blue harness and blue collar.
259.433 -> So into this directory I'm going to create a routes file
262.633 -> and in there are going to paste in an implementation
264.766 -> and we'll just go through that implementation.
266.766 -> So we're going to respond to a GET verb.
269.2 -> This is the new App router version of API routes.
273.033 -> You just specify the different route verbs like GET, POST, DELETE,
276.8 -> PATCH, UPDATE all of that, and then you give the responder.
280.133 -> So in this case, we are going to make a request back to ourselves
284.666 -> to go get a file called whatever color dot JSON.
288.366 -> So let's go over and take a look in the public directory.
290.4 -> And here we have all of our assets, like our colors, our harnesses,
293.333 -> all that, but we also have these JSON files
295.5 -> and the JSON files have a pretty simple layout.
298.233 -> They've got the array of the different products or the names and the thumbnails.
302.466 -> Now, what I'm trying to imitate here
304.1 -> is that you have a back end microservice somewhere that's giving you this.
307.8 -> Now I don't want you to have to deploy multiple servers to imitate that, so
312.433 -> I'm just going to use the public directory effectively like another microservice.
316.333 -> Just think about it that way.
317.6 -> Don't think about it now
318.333 -> as “Oh, I could just make this request of the client so much easier”.
321.6 -> No, no, no, no.
322.2 -> What I'm going to show you is that you need to make a request
324.633 -> from the client back to its own NextJS server in this case,
328.866 -> and then that Next server is going to make the subsequent request
332.533 -> to that microservice.
334.5 -> And that's what we've got here.
335.566 -> So we're just going to make that fetch and then we're going
337.133 -> to return that JSON response.
339.866 -> All right, So let's go make sure that this works
341.866 -> by going over to our arc and then bring up a new tab
345.8 -> and then trying out combinations and blue.
351 -> Perfect.
351.766 -> That's the data that we're looking for.
354.266 -> Okay, So now we need to request it because we have a product carousel ready to go.
357.7 -> It just needs the data.
359.533 -> So to do that, I'm going to go over to our store.ts file
363.666 -> and what we currently have in this store are a set of Jotai atoms
368.1 -> and these are atomic state values
370.066 -> that you can then subscribe to, and they're held globally.
373.3 -> If you're not familiar with Jotia or Recoil. That's okay.
375.9 -> Let me just walk through one example to kind of show you how this works.
378.833 -> So we have a color atom here.
380.133 -> We initialize it with green in a custom hook called useColor,
384.3 -> and that useColor returns the string value
388.366 -> currently as well as a setter, kind of like useState.
392.233 -> So if you take a look over here in the components
397.6 -> in our jacket color selector, when we bring in useColor from the store,
402.133 -> we destructure that tuple into the current color
405.5 -> and then we get a set color and we use it just like anything else.
408.6 -> For example, in this case, we're
409.466 -> just connecting that to our selector, then doing an on change and that's it.
413.566 -> And so anything that can listen to the useColor at
418.466 -> that point is going to get the new value of that useColor.
422.333 -> Okay.
422.566 -> Let's go back to the store
423.933 -> now as two reasons why I want to use Jotai for this example.
426.7 -> The first is that it supports asynchronous atoms.
429.6 -> That's an atom that goes off
431.1 -> and makes a fetch or does something asynchronous
433.066 -> and then says it's value based on the result of that.
435.366 -> The other reason is that Jotai,
437.066 -> like recoil, allows you to create dependencies between atoms.
440.9 -> So for example, we can create a combinations atom
445.233 -> that looks at the color atom, and when the color atom changes,
448.7 -> goes off and fetches the combinations for the color
453.433 -> asynchronously from the end point that we just created.
457 -> So do that. I'm going to bring in some code.
459.633 -> In this case, Atom is a promise, and that promise promises
463.4 -> to give you back an array of objects that have the name in the thumbnail.
467.3 -> Then instead of giving it a value like we do up here with green,
470.766 -> we give it a function in this case and async function,
473.9 -> and that async function gets a getter as the first parameter.
478.833 -> And then with that we can get the current value of the color
482.033 -> and we can use that to make a fetch to our combinations
485.7 -> and then return back the resultant payload.
488.4 -> Now this is a read only atom.
490.233 -> There's no way you can set this atom.
492.1 -> So what we should do when we use it is to use this value and not return a getter.
497.166 -> So to do that we can use useAtomValue, and then we can just
503.7 -> export a
504.6 -> useCombinations that uses use at a value on that combinations atom.
508.766 -> And we can take that and bring it over into our product carousel
512 -> where instead of just assigning combinations to empty array,
516.033 -> we instead use that use combinations from the store.
521.9 -> Okay, so it looks like the data is showing up.
524.2 -> And if we change this color, we get the new values.
528.266 -> That is sweet. That is Jotai.
530.7 -> That's why I love Jotai.
532.633 -> And then I can add to cart here and let's just go into how this works.
537.4 -> After we did this useCombinations.
540.633 -> So down here we have a client component.
543.2 -> It's going to go through all the combinations,
545.233 -> do a little bit of CSS magic to kind of lay it all out pretty nicely
548.733 -> put on an image, put the name and then put in an Add To Cart button.
553 -> And that Add To Cart button is in itself a client component.
557.033 -> You give it a product and then it has a button on it.
560.533 -> And when you click on that button, it uses the set cart value
563.8 -> that it got back from useCart, which is again correlated with that Jotai.
568 -> There's a Jotai atom called cartAdam that this is getting and setting
571.966 -> and it just appends the product to the cart.
575.466 -> Okay, so that's the NextJS 13 standard implementation.
578.5 -> We have a layout that is a server component.
581.466 -> It includes this jacket color selector, which is a client component
584.866 -> because it uses hooks, in this case the Jotai hooks to go and set the values
590.033 -> and then anything nested within that
592.6 -> regardless of it use the hooks are not is going to be a client side component.
596.833 -> So that's red on the outside and then at some point
600.033 -> you're going to go green and everything within that nested is going to be green.
604.766 -> Now the next version of this we're going to look at is a Wakuwork version
608.033 -> that uses actions to create a much easier back end for front end,
613.133 -> where we had to go create our own combinations, color GET route.
618.133 -> It's going to do all of that for us really easily.
621.566 -> Let's go check it out.
622.833 -> This first Wakuwork version of this is in the Wakuwork server functions directory.
628.2 -> So what is Wakuwork?
630 -> Let's just cover that for a second.
631.766 -> What could work is essentially like a NextJS 13 competitor,
636.4 -> but much more minimal and created by Dai-shi Kato.
640.666 -> So it's pretty cool.
642.3 -> So let's go and try this out and see what how it looks.
646.9 -> And we're back to where we were before
648.266 -> at the starting point of our NextJS 13 exploration.
650.8 -> We have a RSC layout and then within
655.2 -> that we've got a client component that has the jacket selector.
657.7 -> We can select different jackets.
659.4 -> It uses Jotai for all of this as well.
661.5 -> We're just missing that product selector.
664.166 -> So how are we going to do this?
665.4 -> Well, what we need is we need to create again, that API
670.6 -> that's going to make the backend fetch
673.6 -> to our microservice in this case again
676.5 -> hosted in public, but it is still that microservice concept.
680.766 -> So let's see what we have in our Wakuwork application.
682.966 -> Currently we have an index.tsx
686.266 -> that starts up our app, we give it a name value customer.
689.8 -> That's where this header for value customer comes in.
692.8 -> And then this app includes the header at the top,
695.6 -> the key down here at the bottom for the server component client component.
699.933 -> And then it has a jacket color selector, which is the client component
704.1 -> and pretty much from there down it's exactly the same.
706.8 -> So this is going to be our root
709.833 -> index for our application.
712.8 -> Now we really want is we want this product carousel to get those combinations.
716.266 -> We get the color in.
717.233 -> Now we need to make a fetch to our local server that will in turn
722.833 -> make a request to the microservice that will go get us those combinations.
728.1 -> So the first thing we need to do is implement that server
731.533 -> side of the fetch to the microservice.
736.166 -> To do that, we create a new file called fetchCombinations.ts
741.9 -> and here's the implementation.
743.366 -> So we have “use server” here at the top
744.866 -> and that's going to tell Wakuwork that this is only on the server.
748.233 -> This function resides only on the server and you you run on the server.
752.266 -> That's why, for example, we have a absolutely URL here
755.466 -> to localhost 3000 because we're on the server.
758.266 -> We need to know what host you want to get that data from
762.766 -> and it could be any host anywhere, including a microservice.
765.1 -> It's going to be us at this point, but that's not the point.
768.133 -> Okay?
768.433 -> So going to make that fetch and that's going to give us back our combinations.
771.2 -> Now in order to use that, we're going to go bring that over into our app.
775.566 -> So we’re going to bring in fetchCombinations
777.3 -> and it's important that this be brought into a React ServerCcomponent
780.9 -> and they're going to pass this through to our product selector.
785.866 -> So the first thing you need to do
786.8 -> is send it to our jacket selector since it wraps the product carousel,
791.6 -> of course it doesn't know it's coming.
793.433 -> So we need to go and add that
797.366 -> and then we need to pass that on
799.8 -> to our product carousel.
803.866 -> Of course, it doesn't know that it's expecting that either.
805.966 -> So let's go and grab that
808.9 -> and then take it and go into product carousel
812.066 -> and we'll just add that in on top of the color that we already have.
815.866 -> So now we have fetchCombinations.
817.2 -> This server side function routed through the React server component,
821.366 -> which is the app through the client component,
824.866 -> which is the jacket selector through the product carousel.
828.6 -> Now we just need to call it to do that.
831.233 -> We're just going to use
832.066 -> a combination of useState and useEffect, pretty standard stuff.
835.333 -> So we’ve got some useState that has the combinations
838.8 -> and we're going to useEffect that's going to make that call.
841.566 -> We'll start off with the useSate.
844.2 -> So define that down here for our combinations.
846.733 -> We're going to say the combinations is an array of the name and thumbnail objects.
851.1 -> And then in our useEffect, and this is where it really gets fun,
854.1 -> we're going to do fetch combinations as just a function.
857.933 -> We're just going to call that combinations as if it's a local function
862.133 -> when in reality it is actually going to make a POST request back to the server.
866.733 -> So let's go see that.
869 -> Okay, Looks like it worked.
870.733 -> So let's actually see in the inspector how this is all coming together.
873.933 -> So we go over here to a network and we change green to blue.
878.133 -> What we'll see is that we have done this action
882.1 -> we called localhost 3000 RSC underscore action ID,
886.933 -> and then we go in a payload
888.466 -> with an array where we have blue is the first item in there.
892.266 -> So basically that's the arguments going to that function.
895.166 -> And then the response that we get back is an array that has all of our data in it.
899.7 -> Pretty cool. Right?
902.1 -> Now the big win here is that we didn't have to write that back end for front end.
905.933 -> We didn't need to go and create our own API.
908.6 -> All we needed to do was create our function, in this case,
911.966 -> fetch combinations and correctly route it through from the React
916.133 -> Server Component to any child component that want to use it.
919.733 -> And then it could use it just like any other function.
922.1 -> And you can do this both for fetches as well as mutations.
926.333 -> Now I learned how to do this by looking at the Wakuwork example code.
929.7 -> I definitely recommend that you read that code
932.633 -> if this is interesting to you and I put a link to it in the description.
936.733 -> Now, this isn't the only framework that implements this.
938.8 -> If you're interested in a closer to production ready version
942.7 -> of this kind of automatic back end for frontend creation,
946.633 -> you should go check out Qwik, which has a $server function and you can
950.333 -> simply just define the function that you want run on the server
955.133 -> right inside of your component and it does it all for you
958.433 -> just right there, which is super cool.
960.533 -> And I would say the DX or “developer experience” on that is better
964 -> than what I'm seeing here, but that's not where Wakuwork ends.
967.666 -> It ends at a very interesting place, which is much more in line
972.033 -> with the React Server Component implementation that most of
976.6 -> you thought that you were going to get, which is a component
980.933 -> that you simply change the properties on and get new content from.
985.5 -> Turns out that React 18 has
989.933 -> streaming functionality that can be reused in a way to make that happen.
994.066 -> So let's try that out.
996 -> So this example is over in Wakuwork RSCs.
998.8 -> So let's fire that up.
1005 -> Now we're back to our starting point again.
1006.2 -> Just like where we were with NextJS 13 and the original Wakuwork example.
1009.8 -> But this time we're gonna do something really different.
1011.8 -> We're going to turn our product carousel into a server component.
1016.4 -> So we're starting out with a product carousel
1017.766 -> that looks eerily similar to what we had before, with two exceptions.
1021.266 -> One, we defined it as an async component like you have done
1024.966 -> with React Server Components before on NextJS 13.
1028.333 -> That means that you can make fetches or whatever you want
1031 -> in this preamble section up here
1033.366 -> and we brought in server component and wrapped it in the server component.
1036.166 -> So get that nice red border indicating that this is a server component.
1041.266 -> So how do we make that fetch?
1043.833 -> Well again, remember this is running on the server,
1046.066 -> so when it's running on the server it can make that fetch directly
1048.933 -> to our microservice.
1050 -> So that's we're going to do, we're going to bring in fetchCombinations right here
1053.4 -> and that's going to call our microservice again, turning the server, this is okay.
1058.266 -> And then down and here we're going to say that our combinations are just awaiting
1063.466 -> the output of our fetchCombinations for the color that is given.
1068.666 -> So now we need to bring in our product carousel into our jacket color selector.
1072.733 -> To do that, we're going to bring in a function called Serve
1076.066 -> from Wakuwork client.
1078.166 -> And then we can invoke, serve and give it then string name of the component
1082.8 -> that we want and the properties that go to that component.
1086.666 -> Now, we have a component here, but it just happens
1089.566 -> to be a server component and not a client component.
1092.866 -> Now one thing we want to do is make sure we only use ProductCarousel
1095.733 -> when we're mounted on the client.
1097.5 -> So we need to track whether we're mounted or not.
1101.4 -> To do that, we create some state called mounted
1103.333 -> and then have a set mounted, and we useEffect to say that we are mounted.
1107.633 -> And then we need to add our product carousel.
1109.566 -> So let's go down to the bottom of our layout here
1112.933 -> and say if we are mounted and then put a product carousel on the page.
1116.9 -> Let's try that
1119.366 -> and it doesn't work.
1120.266 -> So let's go see if we can get some information from our console.
1123.533 -> So telling us we have a 500 on product carousel,
1126.166 -> which probably means that it can't find it.
1128.466 -> So what we need to do is we need to go over here to our entries file
1133.1 -> and we can see that we actually have some interesting entries here.
1135.333 -> We have App, which is it returning The import of src
1138.5 -> App we need to do is add a corresponding one for the product carousel
1142.666 -> and then also down in the prefetch add a prefecther
1145.333 -> for the product carousel, which is prefetched with green.
1149.833 -> And I think that should work.
1150.733 -> So let's give it a try.
1152.766 -> And there we go.
1153.366 -> Now we have our carousel, which is a server component
1157 -> embedded in our client component, which is the jacket selector.
1160.4 -> And now when I hit Add To Cart,
1162.566 -> even though the Add To Cart is a client component,
1164.866 -> it is nested with a server component and it still works.
1167.466 -> Still talk to the same Jotai global store and everything works just great.
1170.866 -> Let's go over to the inspector
1171.6 -> and see what's actually happening underneath the hood.
1173.3 -> So if we go to network
1174.5 -> and I chang, for example, green and blue, we can see that we get this interesting
1179.466 -> request,
1180.5 -> we get RSC and in this case product carousel.
1183.1 -> Then we get the props.
1184 -> So in that case, that's the color blue that we just requested.
1188.3 -> And then we get an interesting response.
1190.166 -> And this interesting response
1191.4 -> is what we see when we use streaming and react server components.
1195.533 -> It's basically saying make these changes to these points in the DOM,
1200.133 -> even though that component doesn't
1203.066 -> exist on the client.
1207 -> So this is exactly what I think folks were thinking about when they were
1210.633 -> thinking about React server components, the jacket color selector in this case
1216.2 -> changes that color
1218 -> and that forces product carousel to update.
1221.466 -> Even though part of carousel is an async component
1225.2 -> that actually resides on the server and it can do all that cool
1229.2 -> async react server component stuff that a React server component can do.
1234.3 -> Now I find this version of React server components to be very compelling.
1237.166 -> If you're on the server server request,
1238.933 -> if you're on the client, then you can have hooks.
1241.4 -> It's really nice.
1243.3 -> So why aren’t we going to get this?
1244.866 -> Well, I've talked to a couple of framework authors and the reasoning is this;
1249.566 -> When you have a server
1252.2 -> component nesting a client component which has a server component,
1255.633 -> you have effectively what they call a potential for a “waterfall”.
1259.6 -> And the idea is that if the nested
1262.7 -> server component or client component ends up changing the state,
1267.633 -> then you can get into an infinite request loop
1270.266 -> as you get new server components that in turn have new client components
1275.833 -> and they cause a state update and so on and so forth.
1278.9 -> And you can get an infinite loop.
1280.866 -> Which is ironic since this isn't
1283.366 -> the first foot gun we have had in React.
1287.166 -> In fact, useEffect is a notoriously bad foot gun for creating infinite loops.
1294.866 -> So why this particular anti-pattern is so much worse?
1299 -> It's not clear to me.
1300.066 -> That's why
1300.466 -> I made this video and these examples and why I want to start this conversation.
1304.833 -> Do you like the next NextJS 13 version with the RSCs that they have.
1308.6 -> Do you like the Wakuwork extension where you can just create actions
1313.366 -> really easily and get those as well?
1317.833 -> Or in addition to all of that, do you like this kind of nested
1323.033 -> RSC style where the components are just like components?
1327.633 -> There just happened to reside on the server?
1330.666 -> All of these models are still available to us.
1332.966 -> NextJS 13 is still in beta.
1335.4 -> And they could add these additional features if they wanted to.
1339.933 -> So let's start a conversation about that
1341.4 -> and see what you think in terms of the architecture that you want
1346.1 -> for React Server Components in NextJS 13 and also in other frameworks as well.
1351.766 -> For example, Remix could be another one
1354.966 -> that is also in the process of developing their React Server Components.
1359.533 -> So put whatever thoughts you have down in the comments
1361.333 -> so we can talk about it there.
1363.066 -> Of course, in the meantime, you have all this code
1364.7 -> available to you in that link in the description right down below rough
1367.866 -> edges and all to experiment with the different architectures
1372.1 -> and see what you like and leave your thoughts in the comments down below.
1376.466 -> And of course I'm always appreciative if you want to hit that like button
1378.833 -> and certainly that subscribe button and click on that bell and be notified
1382.133 -> the next time a new blue collar coder comes out.

Source: https://www.youtube.com/watch?v=ciQdQkT0bZk