>> Scoot Mass: Let's create some issues.
So if I go here, it just goes here, it doesn't do anything.
So, let's make that true.
So, now we're gonna use Server Actions outside of a form,
we don't need a form to click a button, but
we still wanna call a server action, so that's what we're gonna do.
We now wanna create an issue, we'll use Server Actions for that.
Pretty simple so, we're gonna go into our Actions folder.
In this case, we're gonna make a new one if you don't have it already,
called Issues in our Actions folder, we're gonna put this in there.
We're gonna make sure we can create an issue, it's a lot going on there.
And then in our new page when you click the button,
we wanna make sure you can create a new issue.
What we're doing though just to solidify the concept is that
I wanna show pushing down fetching.
So in the case of a new issue, we're gonna be fetching the current user to make sure
that there's a user that's logged in so they can actually create an issue.
So we're gonna push that all the way down to the new issue component.
And then only then if there is a user will we allow you to see the new issue form
with the user ID.
Because also we need to know who the current user is in order to submit
the issue.
So it's another reason and we'll do that.
We're still using a form here, so
there's not much else going on here as far as submitting the action.
We're still gonna use UseActionState, but we'll get into a point where you
can do use transition to be able to call an action outside of a form.
But yeah, I guess in this case I forgot the button doesn't create the new issue,
I still actually do go to a form in which we create the new issue.
So we won't have to cover that part twice.
I just wanna cover the parts that are related to fetching the user and
then calling this action.
So let's do that.
So now we'll go over to our actions.
We have issues here.
We already have the schemas and stuff that I already made.
What we need to do now is create a function for creating an issue.
So let's do that.
And there is a lot going on here.
None of this is specific to next.js, so I really have to go into too much
detail about it, I just want you to know what's happening.
So I'll just copy it, you should too.
It's more the same that we've already done with the previous actions that we made.
So how does this work?
Well, the first thing is it tries to get the current user.
If you haven't noticed, we're calling getCurrentUser a lot.
That's gonna be a performance hit, especially if you hit in database all
the time, but we'll talk about how to solve that.
If there is no user, we're just gonna follow the schema or
our type that we have for the action state we're gonna say get out of here.
In this case, I think yeah, we need to pass in the data here, there we go.
Issue data, in this case, let's validate the issue data using our schemas.
Highly recommend doing this stuff on the server side with Zod.
Once we have that, we're just gonna insert it into the database.
I guess you could validate on a database side too if you want.
But I would never make an API Route Handler without
validating the inputs before they touch the ORM.
So I would do the same thing here.
Database inserts are expensive and
if that's good return success, otherwise get out of here.
So we got that, and then down here I'm just gonna make a handler for
updating an issue.
So I think there's something that's trying to import it somewhere,
but we'll make it later.
Okay, now we need to go to the new page and we need to do a few things.
So one, let's actually make it look good.
So we'll copy this, we'll go to the issues new page.
What is this?
This is that CSS module.
I mean I'm like, what is [LAUGH] I got hacked [LAUGH]?
So we'll put this in here and let's talk about it.
So new issue page, just jsx.
Only thing to note here is that we are suspending a component here.
So we have this new issue component.
This is essentially the form.
This is the form that creates a new issue.
But we don't want to show this form unless there is a user that signed in because
we need the user ID to actually submit the new issue to create an issue.
So we know who the issue belongs to, so we want to suspend that.
So if we go look at the new issue,
this is where we need to get the current user so it can be suspended.
So when you think about Suspense, remember you're only ever gonna suspend something
that's making a call to get data on the server.
So if I'm suspending this new issue component,
that means I'm getting some data in here somewhere.
So I wanna get the user here.
So that's gonna give me the user.
And then what I wanna return here is the issue form.
And it takes a, we'll have to make it, but it's gonna take a user ID.
So I'm gonna suspend that.
And then for the issue form, let's actually create it.
So we go, look at that.
This is essentially, what we just did.
You can do this if it's not a user, you can redirect them, that's fine.
But essentially, we just need to get the data, just get the user ID.
Now that we have that, we got this long form to create an issue.
There's nothing new here that you haven't already seen that I haven't gone
over already.
It's essentially doing the same thing using action state,
using the form, submitting that action, that's it.
Pretty straightforward, it just uses that user ID from its props.
So we can just copy that, go to a new issue form, and we can paste that.
This thing's freaking out cuz it wants update issue.
We could just import that.
That's why I made it.
Even though it's not doing anything right now.
My typescript, folks, there we go.
Cool, so let's just make sure we pass in our user ID,
which is gonna be user.id.
This thing's freaking out because it's like, yeah, what if it's not a user?
Sure, sure, sure, let's do it.
There is no user, then you need to go sign in.
Now, we don't need this.
Okay, let's see what we got here.
This thing says must have.
Must be async functions.
That is a constraint, let's do that, sorry about that.
There you go.
That better?
Cool, so now we can create new issue.
It goes here and you can see that it's loading.
That loading is the suspense of it fetching the user.
That's the fallback that I have for the Suspense.
This is it fetching the user, It's like, cool, I have the user now.
It's obviously delayed.
If I try to create an issue.
Hello there, this is my issue.
I click create issue.
We can see the loading state there.
And then boom, hello there's my issue.
Very much the same pattern that we use to sign in and sign up.
Just applied it to issues nothing different there.
Right, if we walk through that one more time, we start off with our server action.
So we have a server action called create issue.
It just takes in the data, it gets the current user,
it validates the data using Zod, if so it adds it to the database and
then it just returns the success with the message.
And then inside of the page for a new issue,
we suspend the new issue component.
The reason we do that is because it's getting the current user.
So we need to wait until the current user comes back before we show
this component because its children relies on the user being there.
So there is no benefit of showing its children until the user gets back
because its children needs the user.
So don't even show this jsx I don't even show this component until it comes back.
So we suspend it until it comes back, it comes back and
then we show the issue form which is a client-side
component that uses the use action hook and
calls the server action that we created here.
And that's it, everything else is pretty much what you already did.
We're using our forms, we pin our form actions and
we're just iterating over the state messages and errors, nothing crazy.
Same thing we did in Auth.
>> Scoot Mass: Now that you've seen this twice,
you can start to see how some of these patterns can be a little more streamlined.
How you might be able to co locate some of these in like folders and
not have them separated like I have them.
You might determine how you might add to this and
have different naming conventions for a lot of this stuff.
But this is literally the same pattern that I use to do stuff all the time in
next.js.
I have a server action, I have a form or a form action,
I suspend data that needs things and that's it.
[LAUGH] That's like more than half of my app when it comes to like form stuff.
We still haven't made an API route.