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.
👉 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.