server action
server action是一种完全执行于服务端的函数.但它可以被任一组件类型直接导入和调用,就像普通函数一样.尽管写法上完全一致,但服务端组件是直接调用此函数,而客户端组件是通过网络请求,使服务端调用此函数并返回请求结果.server action 必须是异步函数.
写法
在文件顶部使用'use server'表明它会导出server action.如果是服务端组件,还可以采用内联的写法:
export const ServerComp = ()=>{
const serverAction = ()=>{
'use server'
}
return (
//...
)
}
客户端组件则必须导入server action.
作用
server action可以被用于表单(form标签)的action属性.服务端组件也可以使用server action为元素设置事件,或为客户端组件传递参数(即使它不是简单对象).客户端组件可以简单地把它视作网络请求.
注意
server action的参数必须是严格的简单对象.像这样是不行的
// server action
async function Action(){
// ...
}
// 组件
<button onClick={Action}></button>
即使server action的类型声明里不接受参数 但next还是会检测实际提供给它的参数并报错.这个错误在生产环境中也会立刻使页面崩溃. 正确的写法是:
<button onClick={()=>Action()}></button>
但这种写法只能用于客户端 服务端是不允许设置事件的
trpc
server action可以替代前后端的网络请求.但Next14之前 这种写法并不支持,此时应当使用trpc避免直接的网络请求.具体写法也可以看这里
构建trpc
src/lib/trpc/trpc.ts
构建trpc对象
import { initTRPC } from '@trpc/server'
export const { procedure, router } = initTRPC.create()
src/lib/trpc/routes/example.ts
可以执行的服务端函数 使用zod构建
import { z } from "zod";
import { procedure } from "../trpc";
export const example = procedure
.input(z.object({ message: z.union([z.string(), z.number()]) }))
.query(async opts => {
return opts.input.message
})
src/lib/trpc/index.ts
注册服务端函数 导出可以使用的对象
import { router } from "./trpc"
import { example } from "./routes/example"
export const TRPC = router({
example,
})
src/app/api/trpc/[trpc]/route.ts
实际进行网络请求的部分
import { TRPC } from "@/lib/trpc";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: "/api/trpc", // 这个与当前路由有关
req,
router: TRPC,
createContext: () => ({}),
});
export { handler as GET, handler as POST };
src/utils/trpc.ts
创建trpc句柄 仅限客户端组件使用
import { TRPC } from "@/lib/trpc";
import { createTRPCReact } from "@trpc/react-query";
export const trpc = createTRPCReact<typeof TRPC>()
使用
上下文
提供trpc上下文 应当在root layout中使用这个组件包裹子组件
'use client'
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink } from "@trpc/client";
import { ReactNode, useState } from "react";
import { trpc } from '@/utils/trpc'
type Props = {
children: ReactNode
}
export function TRPCContext(props: Props) {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: '/api/trpc',
}),
],
})
)
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
{props.children}
</QueryClientProvider>
</trpc.Provider>
);
}
客户端组件中
import { trpc } from '@/utils/trpc'
// 组件内
const res = trpc.example.useQuery({ message: 'trpc example' })