I understand the use of server actions in client components, for form submission for example or any other mutation.
As for data fetching, in server component we can directly access the db so that’s great. And in client components it’s a bit more tricky but there are still great solutions, the one I’m using is the trpc + ReactQuery combo.
However I often see people using server actions inside server components, And I cant figure this one out, because we can directly access the db as mention before.
So why not just create a simple function call that does that mutation. Why use server actions in server components?
Maybe I’m missing something, but I couldn’t find any good reason to do so.
I tried both ways and there seems to be no significant difference in performance or any other metric that I could think of.
2
Answers
Yeah, if that’s all they’re doing then they’re doing it wrong. The reason you would want to do that is that unlike regular functions server actions can be serialized across the client/server boundary and called from client components:
Here were passing the server action function as a prop to the client-only component, the client can just call it normally and the build process/runtime handles the serialization logic across the client/server gap without actually exposing the db interface to the client.
So server actions can execute even in scenarios where JavaScript is disabled within the browser.
server actions are just functions so they are reusable. you can use the same action from client and server.
from the same docs above:
isPending
state status. if user is sending a mutation, you can show the state of the mutation while user is waiting. you could wrap the server component withSuspense
component and show the fallback but this is very expensive operation. Suspense fallback replaces the entire component tree inside the boundary with a single fallback, which can be great for full-page or section-level loading. if your component does mutation or fetching multiple times this will be very expensive for your application.isPending
lets you handle loading at a more granular level. For example, you could disable a button or show a small loading spinner next to the button without wrapping the entire component inSuspense
.