>> Scott Moss: Now we need to go to our issues id page and get the issue.
Well, first get the Params.
Talk about that.
Get the issue from the Params and then from there go ahead and
hook everything up.
Everything here is pretty standard.
There's nothing in here that's super specific to Next.js.
We're not suspending anything or anything like that.
It's pretty straightforward, it's all just read only data.
So, nothing new to learn there.
But the important part is this, especially this part cuz this is actually new.
This part right here,
it looks a little strange cuz you're just awaiting a thing and not a function.
This is relatively new and latest version of Next.js.
So let's go to our issues page,
which should be issues [id]/edit page right here.
I'm sorry, no, we need to make a page for the issues page right here actually.
I don't have it.
So you need to make issues id and then page.tsx here and
that's where our issues page is gonna be.
I'm just gonna grab these imports cuz it's a lot,
put them in there and then we can just make our page.
So export, I'll say,
const IssuePage,
export default IssuePage.
So all pages by default have access to the request.
So you can get the request through the props.
You can also get access to in this case params.
And the reason we get access to params is because this page,
it is a route of a dynamic segment, this id right here.
So we get access to the params.
So here we can actually get params like this.
What's new in the latest version of Next.js is that params is async,
so you have to await it.
This literally wasn't the case in Next.js 14.
So if you don't await it, you'll actually get an error in the console.
I mean, I can show you but the reason I'm telling you that is cuz I know people here
are really crazy about typescript.
So when you type it, make sure you put a promise around it,
and then for the type, just look at your routes.
I have an id here.
If this was a child directory of another dynamic segment,
I would put those properties in here as well.
But there's only one, it's id.
So I'm only gonna put id and we'll put string this.
There we go.
So now that we have that, we can get the id from params.
So I can say id = await params.
Make sure I put async on this.
There we go, now we got the id.
It's gonna comment these out.
So I'll use them cuz it's annoying.
So once we have the id, we can go ahead and get the issue by that id.
So I'll just say const issue =
await getIssue which comment that out.
And I'm going to parseInt this id.
The reason I'm doing that is because in the database all ids are numbers,
but in route parameters everything's always a string.
So I have to convert it to a number.
So I'm using parse integer to do that, okay?
If we do not have an issue, because again, this is a server
component which is essentially a request, I can just do a notFound.
This right here will redirect the user to a page that Next.js
by default already creates for us, which is a Not Found page.
You can override this page by making it yourself, but this will just redirect to
the Not Found page, which you can override by doing a 404.tsx or js.
So this will do that.
You don't have to put a return statement here because it's never going to go
parse this no matter what you do.
So we're good there and then we have all that.
We can just grab all this.
There's nothing important here that's specific to Next.js.
Cool and let me uncomment all this stuff.
Great, so the last thing is we need to go make the getIssue function
from the data access layer because it does not exist.
So let's go do that.
Did I not add that in here?
That's all right, we'll just make it ourselves.
So if we go here to the data access layer, we have getIssues.
We can just make getIssue.
GetIssue is gonna take an id which is a number and
we're just gonna say issue = await
db.query.issues.findFirst, where,
The issues plural, which is the table, .id is equal to the id that you passed in.
Oops, I need a comma there.
There we go.
So let's try catch this.
Well, I guess I'll just return it.
It doesn't matter, it'll be null or not.
Cuz we're already handling it in the component if it's null or not.
So I'll just return that and then if it's error, say console.error.
And I guess I'll just throw the error again.
I actually want this to break it.
Well, no, I guess I'll just return null if there's an error.
That way you can do a notFound.
>> Learner: I have a question.
>> Scott Moss: Yes. >> Learner: Any more of a drizzle question
anything?
Sometimes in seeing queries like for the getIssues.
DX's layer function above it, I've seen with user: true.
>> Scott Moss: Yeah.
>> Learner: What is that doing?
>> Scott Moss: It's a join table.
>> Learner: Okay. >> Scott Moss: Yeah, so
if you go look at the schema,
the issues has a one to many relationship with the user.
This is just saying include go join that user.
Which I guess technically this would be a left join or something like that.
So that's essentially what it's doing.
>> Learner: Got you, so it's just including the user info on your?
Got you.
>> Scott Moss: Mm-hm, yep.
>> Learner: So you don't necessarily need that here?
>> Scott Moss: No, yeah, I don't need that here.
I don't know if I even need it here.
[LAUGH] I just did it.
[LAUGH] I don't even know.
Cool, any questions on the on the getIssue?
Okay, so if we didn't break anything, we should be able to
go look at an individual issue and hopefully that works.
So let's just see.
No errors here, that's always good.
Go to our Mode, new refresh.
If you don't have any issues, create one first and then click here.
>> Scott Moss: Let's see, uh-huh.
You guys should know this error by now.
This is good.
>> Learner: It's the dynamic I/O one, right?
>> Scott Moss: Yeah, so what is the issue?
>> Learner: We gotta suspend it.
>> Learner 2: Yeah, you gotta suspend the page.
>> Scott Moss: We gotta suspend it, right?
>> Scott Moss: There's two of them actually.
So, what's this one?
It's gonna read properties, I don't define reading email.
Okay, we'll get to that one.
Okay, so yeah, this is the new feature, right?
And I'm glad that they actually do this because like I was saying before,
if you don't [LAUGH] opting out of the caching that's invisible,
that's happening for you all the time.
Is so hard to get around whereas this is just super straightforward and
it just tells you what to do.
So first we have to understand where the data fetching is happening.
And sometimes it's not as obvious as you would think, but
in this case it is pretty obvious.
The reason I'm saying that is because if you push down the data fetching like we
did with the navigation sidebar, with that email.
If you push it all the way down,
sometimes it's not as easy as just suspending the page.
You got to go all the way down to where that is and suspend that or use a cache.
So that's why I'm saying that, but anyway, cool.
Do you guys remember the three ways I showed you how to add a suspense?
>> Learner: In the layout you could add it.
>> Scott Moss: Mm-hm.
>> Learner: Or loading a loading component.
>> Scott Moss: Loading component?
Yeah, what was the other one?
Do you remember?
>> Learner: Well, that one suspended that would actually use the cache.
Is there another way to suspend it than those two?
>> Scott Moss: Yeah, it's the way that you-
>> Learner 2: Writing the component.
>> Scott Moss: Yeah, you just do write in the component.
Yeah, so for this page.
So this is the page that needs to be suspended, right?
So this needs to be suspended.
So I don't recommend doing this, but
I could just take literally all of this, right?
Putting in another component and then put that component in here and suspend it.
So it's like don't do that.
[LAUGH] Yeah, the simplest way is just to add a loading.
So we'll say loading.tsx like this and
then I'll just say const or yeah, const.
Let's just call it IssueLoader = some components.
Remember, this loading component is not the suspense component.
It's the fallback for the suspense component, right?
By adding a loading file, you're telling Next.js to wrap your
page in a suspense, and then use this component as a fallback.
So remember that.
So you won't be putting suspense in here or anything like that.
Let's say loading.
>> Scott Moss: Cool, so fix that error.
Now we have another error.
This one is, cannot read properties of undefined (reading 'email').
Looks like it happened on the server side maybe.
>> Learner: I think it is asking for that user join.
>> Scott Moss: Maybe it is asking for the user join, yeah.
Let's see, let me hit Save right quick to make sure I actually save everything.
Cool, yeah, maybe it is.
So in this case we will go back to our data access layer and
we will say can you give us the user.
Like that.
See if that fixes it.
Yeah, I guess it did need the user.
I had a really nice skeleton for this, but I don't know what I did with it.
And just so we can see the loading, let's go ahead and
simulate some delay here cuz it's obviously just moving way too fast.
So I'll go to getIssue.
I'll add a mockDelay of some time and there's our loading.
Yeah, I've been using Drizzle for about a year now.
I switched from Prisma, which I think is good.
But I switched from Prisma because the migrations were just really hard to do.
[LAUGH] With Drizzle you can do them programmatically, it was just so
much easier.
But since then I've learned how to actually prevent myself from getting into
a broken state on the database.
So I don't commit breaking changes on the database.
So I probably could go back to Prisma now that I actually know how to make
proper changes.
But Drizzle is actually just so good, I just love it.
>> Learner 2: So what a drawback of doing the loading.tsx?
Be that you can't really target a really specific component for suspending, but
you're just doing the whole page.
>> Scott Moss: Exactly.
The loading is for the entire page, which would not work if you were
pushing the loading all the way down to a leaf component.
So yeah, you have less accuracy and it's a UX thing too, right?
Look, the whole page is blocked until I get the issue.
Whereas if I was a little clever about this,
what on this page does not need an issue to render?
Well, this button doesn't need an Issue.
This button doesn't need an Issue.
All this is always gonna be the same, no matter what issue is on the page.
This box will always be here no matter what.
This box will be here always no matter what.
So I could- >> Learner: You can skeleton in and out.
>> Scott Moss: Exactly, so you get that flexibility.
But then you gotta push it all the way down and you have to do all that.
But yeah, ideally that's what you would do.
>> Learner: Here's a more client-focused question.
So you get to this page from previously looking at the full list, right?
Let's say you wanted to render the title of the issue but
then load the details later.
>> Scott Moss: Mm-hm.
>> Learner: You could do a combo of maybe saving the title and client side state.
And then navigating over having the details be in a server component and
then calling those up afterwards.
>> Scott Moss: Well sure, if you're coming from this page.
>> Learner: Main list. >> Scott Moss: Yeah.
>> Learner: You wouldn't know if you were refreshing the page.
>> Scott Moss: Right, yeah. If you refresh the page and yeah,
you wouldn't know.
Coming from this page, sure,
you could save it to the client because this is a client side route using link.
So it takes us to the client side and then you could get it.
But because it is client side,
you would have to wait until the client bootstrapped to get that.
So there would still be a delay.
So I don't think you wouldn't actually notice a difference because the client
bootstraps after this.
Then it would get the client side state.
>> Learner: Okay.
>> Scott Moss: So, it still really wouldn't make a difference.
Yeah, the only way to do it is the way that I said that
you'd have to do it, yeah.
I mean, I guess a good way is this.
I guess, you could do this.
So you can put a layout here and
in that layout you have these static elements that are always here.
That might be something you could do.
But yeah, I think the way that I would do it is,
I would just break down these components to do their own call to get an issue.
And then we haven't talked about it yet, but we'll talk about it soon,
is then I would cache that get issue function.
So even though this component called getIssue get an id and this component
called getIssue with the same id, it's only ever gonna call it once.
>> Learner: Gonna call it once.
>> Scott Moss: Yeah. >> Learner: Cool, okay.
>> Scott Moss: So that way they can load independently and
not know about each other, but still get the same data.
>> Learner 2: Stupid question.
>> Scott Moss: Yeah. >> Learner 2: If you have nested
dynamic routes, do parameters get nested as well?
>> Scott Moss: If you have nested dynamic routes, do parameters get nested?
I'm pretty sure they remain flat.
Where am I grabbing the parameters?
Here, so in this case, let's say we had another dynamic route above here and
let's say it was called, I don't know.
What's a good parameter value?
>> Learner: Locale.
>> Scott Moss: What'd you say?
>> Learner: Locale.
>> Scott Moss: Locale, okay, locale, right?
So then it would still just be flat.
It would just be locale like that and you could just grab it.
So it wouldn't be in a nested object,
it would just all be flat on parameters, yeah.
Now you might get an array of parameters if you do a catch all.
So if your route is a catch all route, then this would not be a string,
it would be an array of strings.
I think that's it for creating or deleting and editing the server actions.
Pretty straightforward.
I mean, not much is different from what we kinda already went over.
But what I wanted to hammer home is just the pattern.
At least I just wanted to show you my pattern of how I go through and
use the server actions to respond to a client interaction.
And then use the data access layer functions to grab
data during a React server render.
So, pretty simple.
And I think that's pretty much what the Next.js docs recommend as well.
And again,
the easiest way to think about it is just replace server actions with API calls.
If you just mentally replace that, it's like, okay,
I was going to do a fetch here.
I was gonna do a thing with React query.
It's like, now instead I'm gonna do a server action.
Yeah, it works.
And to me, it's crazy for me to hear myself say that.
Because it was, I don't know, a year and a half ago where I was like,
I don't know why people would use server actions.
These are just not, don't use them.
But they've come a long way and I have definitely changed my mind on them.
And I honestly think it has little to do with Next.js cuz they haven't really
changed much as far as the DX for server actions.
It's more about React.
React has added really good hooks.
The biggest one was being able to do async await inside of a transition hook,
whereas you couldn't do that before.
So I was like, how do you await some async thing?
What a transition?
So yeah, it's really good now.