Can React 18’s new cache function save the use hook and give us the use(fetch()) pattern that we didn’t ask for and probably won’t use instead of react-query or SWR?
👉 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 Introduction 1:21 Project Setup 2:50 Using The Use Hook 5:53 Trying Out Cache 8:39 Caching Fetch 10:22 Sharing The Cache 13:46 Bringing In Context 17:12 Architecting Around Cache 18:26 Summing Up 19:26 Outroduction
Content
0.233 -> With React
18.2 and NextJS 13 we got a new “use” hook
5.3 -> and with it the potential
of doing something cool like this
8.766 -> inside of our data fetching components
where we just put a fetch inside of a use.
14.166 -> Just like that.
14.966 -> And then use, which monitors promises,
18.033 -> will give us our data
and that makes our components super clean.
22.633 -> But what I tried to do that back
a couple months ago, this particular code
27.466 -> would cause an infinite loop of requests
immediately and render nothing.
32.7 -> And when I put that into a video,
folks said, oh wait, just you wait.
36.533 -> There's going to be a cache function
that comes out in React
39.633 -> and it's going to fix everything
and give us this cool use fetch pattern.
44.166 -> Well, cache is out,
so let's go into NextJS 13,
49.2 -> try it out
and see if it actually does fix this
52.866 -> and in the process
get a really interesting new way of again,
56.833 -> sharing data within our React tree
between components in different spots.
62.033 -> Oh, and by the way, before I forget,
I just want to say thank you
65.033 -> to the NextJS team
who stepped up on Twitter
67.3 -> to help me out yesterday,
walk through some of these issues.
69.9 -> I really appreciate that.
Thank you so much for that support.
72.633 -> All right.
73 -> Let's jump right into it.
78.666 -> This isn't going to be one of those videos
79.933 -> where we make some cool new project
in this one.
82.1 -> We're literally just going to look
83.266 -> at the primitives of stuff
and see how it all works.
86.466 -> So to do that, we're going to need
a NextJS 13 application.
89.4 -> So I’ll create that using npx,
create next app with nextjs-react-cache
94.233 -> as the name that's going to be the name
in GitHub, in the link in the description
98.366 -> right down below.
99.466 -> So you can get access to all that source
code of course, for free.
102.933 -> And we want to
103.533 -> use the experimental app directories
so you get all the cool new stuff.
107.4 -> All right, let's try this out.
108.9 -> TypeScript, eslint, src, yes, please.
111.166 -> And the import alias,
112.233 -> which is going to give us @ slash,
and that is the import for source.
116.8 -> So anything after that
and you get a src file.
119.733 -> Cool. Awesome.
121.833 -> Okay, let's bring that up in VX code.
124 -> Now we are going to be doing
a lot of fetching in this one,
125.833 -> so I'm going to go create some data
over here in public.
128.366 -> I've got three different customers
1.json, 2.json, 3.json
133.566 -> that have some fields in them first
name, last name, job, email and so forth.
138.5 -> So this is what we're going to get by
just doing /1.json in the code
144.166 -> and also I've gotten some feedback
that folks don't like it when I switch
147.266 -> between a black VS code
and a white browser screen.
151.666 -> So I'm going to go and put in some CSS
it's going to make it nice
154.8 -> and dark for us.
159.2 -> All right, now I'll go over to page.tsx
and pair that down
162.3 -> to just the basics.
167.633 -> Now, this is a React Server component.
169.266 -> You can't use hooks in a React Server
Component and we want to use the use hook.
173.133 -> So I need to turn this into a client
or hybrid component,
176 -> whichever way you want to say
it. To do that, I need to say use client
179.966 -> and then I can bring in the use hook.
182.866 -> All right, let's fire this up.
186.866 -> All right.
187.1 -> Now, that's on point 3000,
which is good to know.
190.6 -> And it got a nice blank black page. Cool.
193.166 -> All right.
193.4 -> So now we won't get that nasty flickering
back and forth. So.
197.133 -> Okay, so we're going to get data
200.7 -> from use
202.8 -> and use is going to use a fetch
207.2 -> and we'll go and get number 1.json
210.2 -> and then we'll put that out as data.
216.233 -> Okay, let's check.
220.5 -> Nice.
221.066 -> Okay, so let's go check it out
the network and see what's going on.
225.333 -> If I refresh, you can see that
we have 1.json that's really cool.
229.566 -> So what's happening here is
the NextJS has wrapped the native fetch
234.3 -> and replaced it
with a fetch wrapped in cache
239.666 -> and we'll get into what
the cache function is and how it works.
242.833 -> And that's something to know,
apparently it is an opt out thing.
247.433 -> So you can not do that if you don't
want their cached version of fetch.
253.166 -> But here's an interesting thing.
255.266 -> If I go back over to the code
257.766 -> and let's say I make
258.566 -> one little change
adding a query parameter on there, right?
262.4 -> This is no problem.
263.633 -> We're still going to get back to the data.
265.6 -> I’m gonna hit save.
266.166 -> If I go back over to Arc.
269.1 -> We can see. Boom baby!
272.533 -> Infinite loop requests.
276.033 -> Not great, right?
277.633 -> So this is exactly what we were seeing
before with the old use.
281.166 -> I'm going to actually go back over
into my Visual Studio code and disable
284.633 -> this as quickly as I can,
so don't take down my browser.
292.333 -> So what happened?
293.066 -> Well.
293.266 -> What happens is every time we render home,
296.466 -> we get a new promise reference
coming out of fetch.
302.466 -> And then the
then and that goes off to use.
306 -> Use says, oh, I have a different promise
now, so I’m gonna await that.
310.4 -> And then we would
then render and then that
315.066 -> promise resolves.
316.533 -> We get data again and then that starts
a re-render cycle again.
320.7 -> And so we get an infinite loop of request
323.966 -> because yes, the fetch is the same,
but the promise reference
328.766 -> coming back is different each time
and therefore we get the infinite loop.
332.433 -> If you're new to this channel,
you probably haven't heard me
334.233 -> say this before, but references
and understanding them is key
339.3 -> to understanding JavaScript and TypeScript
and React.
343.033 -> It's never about the content of the array,
object, or function or promise.
347.566 -> In this case, it's
about the reference to it.
350.7 -> So I think we can fix this with cache,
but that's because I know what cache does.
354.866 -> Not sure you do.
356 -> So let's go through
what cache does. Let’s bring it in.
359.966 -> So cache takes a function.
361.966 -> We'll create a new function
that is called adder.
365.2 -> It's going to take two values
368.4 -> A and B and add them together.
371.366 -> And you know what?
371.833 -> Let's just do that a bunch of times.
377.5 -> We'll do one, two, three, four,
381.2 -> and five, six, and I'll hit refresh.
384.7 -> And so I have 3, 7
and 11 over the browser.
387.166 -> Cool.
388.966 -> And let's put in a console.log here
390.666 -> so we can see how many times
it was actually run.
395 -> All right.
395.266 -> So using the free VS code extension
console ninja, I can see that
398.533 -> we got three invocations here,
401.7 -> one for 5, 6, one 4, 3, and one for 1, 2.
403.5 -> Exactly as we would expect. Awesome.
406.333 -> So let's try this again.
408.366 -> Let's do a bunch of these and let's save.
411.033 -> That's going to cause Arc to refresh,
which is going to make the request again
414.4 -> is going to generate a console.
415.466 -> And we can see
now that we got 12 requests,
417.633 -> I guess I copy and pasteed that three,
four times, whatever.
421.033 -> And now we have 12 of them.
422.233 -> All right.
422.666 -> So if we were to take this function now
and wrap it
426.266 -> and cache,
429.766 -> what we expect is to go back to three.
433.2 -> So what cache does is it takes a function
and it returns a function that wraps it.
438.933 -> And for every invocation,
441.166 -> it looks at the incoming arguments
in this case like one, two, whatever,
445.733 -> and it looks to see, have
I seen that before?
448.2 -> And if it has seen it before,
450.666 -> then it returns
the value that it has stored from before.
454.466 -> If it hasn't seen it, then it runs it,
stores that value and returns a new value.
459.2 -> And that way
we should only get three invocations here.
462.433 -> Let's try it out.
464.266 -> There we go.
465 -> Three. Awesome.
466.366 -> So what we now have in React
469.066 -> cache is a very standard
473 -> memorization function
476.066 -> in this is where we get
into the world of naming
480.166 -> because we had a memo function
and now we've got a cache function.
485.766 -> Except that the memo function
wasn't really ever a memoization function.
489.566 -> It was a way to mark components
as kind of memorized,
493.3 -> except that it was only a one level memo.
495.9 -> This new cache is actually a true
memorization,
498.766 -> and the original memo
is more like a cache.
501.366 -> And, ahhh. Okay.
504 -> Anyway, we just have to know
that this is what this does.
508.433 -> Cache takes a function
and returns a memoized function.
512.333 -> Sorry. Naming.
515.066 -> But we don't care about adding numbers
together.
516.6 -> Do we?
So let's get rid of this at our stuff
520.166 -> and let's go and wrap
521.733 -> our fetch in this cache.
525.9 -> So we'll call this fetch user
528.2 -> and replace the contents here
531.233 -> with our fetch.
532.866 -> We'll take an ID
534.6 -> which is a number,
537.3 -> we'll turn that into a string template
540.666 -> and we'll use that ID
542.466 -> as the number for the request.
546.1 -> Okay. Looks pretty good.
548 -> So now let's go back and try this out
and see if we blow up again.
552.666 -> So uncomment this
556.1 -> and then bring in our fetchUser.
558.833 -> You have an ID, 2, in this case.
562.533 -> Now we've got our data and let's
bring back our JSON.stringify. If I
567 -> okay, looks good
568.366 -> and let's inspect that, see, let’s
go over to our network
571.933 -> and now we only get our 2.json
request once.
575.566 -> That's really cool.
576.833 -> Originally when we were looking at this,
we got that JSON request twice
581.1 -> and that's
because we are in the strict mode
583.933 -> and we're getting mounted on mounted
and re-mounted on the client.
586.8 -> So now we're not only just
getting one request,
591.1 -> but we're actually getting one request
really even with really strict mode.
596.3 -> Okay, let's try and do my little thing
here where I add on the query parameter,
600.6 -> see if that hurts it.
602.066 -> I hit save.
603.033 -> Go back in Arc. Nope. Looks just fine.
606.3 -> Solid as a rock.
608.266 -> Yes, I love it.
610.133 -> Except that we've had to do the work.
So. Yes.
612.6 -> Did cache save use?
616.166 -> I guess.
617.8 -> Okay. Anyway.
619.033 -> All right, movin’ on.
620.933 -> So what I want to do is
I want to have two different components.
624.166 -> I want to have FirstName
and I want to have LastName.
626.5 -> First, in this case would show Jamie,
last in this case would show Lee.
631.133 -> And I want to get it from a single user.
634.2 -> So how are we going to do that?
636.4 -> Let's go and build our components first.
639 -> Okay.
639.3 -> New directory components
641.6 -> and into the first name component.
645 -> So we're going to get some data somewhere.
646.733 -> Are we getting an ID
and we got to get that data.
649.533 -> So let's go and bring that in from Page.
652.7 -> We’ll just bring the whole thing in. Cool.
661.433 -> So now we have basically exactly
it was on page.
663.9 -> So the we have first name now
and we're only looking at the first.
667.9 -> All right,
let's go and bring this into our page.
679.233 -> And we got to give it the ID
684.1 -> and there we go.
684.9 -> Cool.
685.333 -> All right, let's go
and do the same thing for last name
694.466 -> and we'll go over
695.266 -> and bring it in.
702.9 -> Awesome. Ops.
704.133 -> Still says “First”.
706.033 -> Okay, let's go back over here to our last.
709.166 -> All right. So that worked. Well,
we are getting two requests.
711.833 -> So why is that the case?
712.766 -> Well, what the deal is, is
you might be like,
716.566 -> well, wait a second, They're working off
the same cache function, right?
720.766 -> No, they're not.
722.333 -> They're not working
of the same cache function
723.8 -> because we're creating fetchUser here
and we're creating fetchUser here.
728.6 -> They're exactly the same implementation.
730.533 -> They're not the same reference.
732.833 -> So what we need to do is have them share
the actual fetchUser function.
738 -> So let's go do that.
740.3 -> So I'm going to go in and extract,
fetchUser here
742.566 -> into a lib directory.
746.666 -> Well create
746.966 -> new file in there called fetchUser
751.5 -> need to bring in cache
754.8 -> and export that.
757.6 -> Okeydokey.
758.466 -> Let's go over here,
763.333 -> get rid of our fetchUser
769.5 -> and then also bring in here
for first name.
773.366 -> Awesome.
774.066 -> Now let's try it again
776 -> and now we get a single request
778.1 -> because we're using the same
that user in both places in there.
782.033 -> Cool though.
782.9 -> So here we go.
783.9 -> Let's go back and take a look at the code
we've got up here, the page directory.
787.5 -> We aren't sharing any
anything between these two.
791.433 -> There's no global store manager.
792.666 -> There's no context. There's none of that.
794.933 -> And yet we're set making a single request,
798.2 -> but getting back data that runs
both of those components.
802.866 -> And it's a new way to think
about sharing state
805.433 -> in different parts of the React Tree,
808.366 -> where we used to use something
like a global state manager or context.
812.1 -> Now we can think about
just having a shared cached
816.366 -> API request system where it just dupes
the requests automatically for you.
823.2 -> How cool is that?
824.9 -> But what if we want to add up here
827.4 -> to not actually have to pass in ID?
830.3 -> What if we wanted to actually
have that be through context?
834.166 -> Really.
835.366 -> So let's create some context
so we can pass down
839.1 -> the ID to first and last name
and we can kind of get the sense of
842.7 -> if you were to use context,
what you now have to put into context,
846.633 -> which is less than what you had to before.
849.633 -> All right,
let's go and create user ID context.
856.566 -> All right.
856.9 -> I’m just gonna bring in an implementation
for us.
858.566 -> We create a context.
860.1 -> It's going to have just be the one number,
the user ID and then it's going
863.7 -> to give us back a component which we can
then use.
866.766 -> User ID provider, we give it
the user ID, we wrap children in it
871.2 -> and we pass along value
and then we can use
874.333 -> the useUserId hook to get these right.
877.766 -> All right.
878.1 -> Let's let's try this out.
884.433 -> We'll give it a user ID of 3 this time
888.2 -> and then we'll take out these IDs.
891.5 -> Now go over into our first name
and last name,
894.533 -> take out the requirement for the user ID
898.333 -> and we'll bring in our custom
899.3 -> hook to get the user ID, drop
902.433 -> that in there.
905.633 -> And so here's an interesting point.
907.2 -> So user ID,
909.433 -> we default to null over
in our context, right?
912.333 -> So we set up a null here
and the output of use user ID
917 -> can be either a number or null.
919.5 -> So what do we do here?
921.566 -> Because we want to fetch a user.
If that's no,
924.5 -> this is where one of the interesting parts
about the use hook in particular
927.333 -> comes in.
928.233 -> The use hook in
particular can be conditional.
931.566 -> So we can do something
like this. We can say
934.866 -> we'll get the ID
936.866 -> and if there's no ID where it return null,
940.8 -> otherwise we go on to our fetchUser.
944.433 -> This would have been a
no no with any other hook
947.633 -> because the rules of hook state
that you always have to have
950.233 -> the same number of hook invocations
on every single render of the component.
955.533 -> But the use hook
actually doesn't require that.
958.666 -> So we can do cool
conditional hooks like this.
962.4 -> Awesome.
962.8 -> Let's go over and just basically copy
again this in the last.
966.5 -> And so there are a lot of revisions
and then change that around again
969.5 -> the last name.
973.166 -> Okay, let's see.
974.6 -> Tada!
975.666 -> Works is great. And to test it out
just a little bit further
978.633 -> to show you how this caching works.
980.9 -> I'm going to bring in some buttons
982.066 -> that allow us to flip between one,
two and three.
985.2 -> So I need some state for that.
987.8 -> I need to have that user ID as state.
991.5 -> Start off at one, say.
994.366 -> And then I'll paste in a bunch of buttons.
996.3 -> And then one last thing
I need to actually use that user ID.
999.166 -> Okay, There we go. Let's go back into Arc.
1001.5 -> Hit refresh.
1002.9 -> And we start off
with Sally Jones. Awesome.
1004.833 -> So we get 1,json, 2.json, 3.json.
1009.033 -> But now what happens
if I go back to 1.json again?
1012.333 -> No difference.
1013.7 -> Why is that?
1014.833 -> Well, what's happening is that cache
because it's getting exactly the same
1019.2 -> ID it did before is giving us back
exactly the same promise.
1023 -> And when we ask that promise to resolve,
it's just giving us back that data.
1027.1 -> It is in fact a true cache,
which is really cool.
1031.866 -> So let's think about this
from the application
1033.233 -> architecture perspective
for just a second.
1035.5 -> We have a context.
1037.266 -> That context has, in this case,
just the ID of the user in it.
1041.866 -> And then we have this fetchUser
which allows us to go
1044.7 -> and make that service call,
but do it in a cached way.
1047.833 -> Now let's compare that to what we would
have had to have done without cache.
1051.466 -> Assuming we just had context,
we would have to take all of the data
1055.433 -> from this fetch user
and put that into the context as well.
1059.333 -> So the context would have had a user ID
1061.133 -> plus the first name last time,
email, whatever,
1063.566 -> and now we can just have
the shared piece of data
1067.3 -> that one little piece of state,
that user ID
1070.066 -> and when it comes
to getting specific data,
1073.333 -> we can make service requests like this
and be sure that they're cached
1078.3 -> and we'll always be able to get efficiency
when it comes to our request
1082 -> to the server. Pretty awesome.
1083.9 -> Now if you notice, I'm
not doing anything on the server side here
1086.533 -> and there actually is a bunch of stuff
on the server side around this.
1091.3 -> Doesn't use “use” obviously,
but does use that cache fetch
1095.666 -> in a way very similar to this.
1097.633 -> And so if you're interested in that,
please put in the comment section
1100.2 -> down below that you want to see
how this works on the server.
1104.033 -> In summary,
we've taken a look at the new “use” hook.
1106.4 -> We’ve
taken a look at the new cache function
1109.633 -> that you can then wrap fetches with?
1112.733 -> Fetch of course is wrapped,
but it doesn't seem to give us
1116.066 -> that use fetch functionality
that folks were looking for.
1120.366 -> Honestly, I'm not looking for that.
1121.8 -> I think I would rather use, useQuery
or use
1125.533 -> SWR for that
because those have extra stuff.
1130.033 -> Like did we get an error
or are we still loading?
1133.333 -> The refetch stuff.
1135.133 -> All of the mutation stuff.
1136.833 -> It's just a much better interface.
1138.166 -> So I think use fetch is a cool idea,
but it really only works in the happy
1142.133 -> path case and it doesn't give us
a whole bunch of extra functionality.
1144.966 -> So I'm going to leave it alone.
1147.066 -> Even if we got it.
1148.333 -> But I do think that this cache
1151.366 -> used responsibly is very, very cool.
1154.466 -> I do think though that in a SPA context,
1158.233 -> when you have a long running session,
figuring out how to invalidate
1162.433 -> parts of this cache
is going to be really important.
1166.433 -> All right. Well,
I hope you got a lot out of this.
1167.9 -> If you did, please hit that like button.
1170.433 -> If you really did, please hit that
subscribe button and click on that bell
1174.4 -> and you’ll be notified the next time
a new Blue Collar Coder comes out.