React - 服务器操作
反应表单动作。
React 引入了新的表单 Actions 和相关的钩子来增强原生表单并简化客户端-服务器通信。这些功能使开发人员能够更有效地处理表单提交,从而提高用户体验和代码可维护性。对于React form Actions的深入探索,你可以参考我关于React Form Actions的文章中的详细文章。
服务器操作
React 18 引入了服务器组件功能。服务器组件不是服务器端渲染(SSR),服务器组件在运行时和构建时都在服务器上专门执行。这些组件可以访问服务器端资源,例如数据库和文件系统,但它们无法执行事件侦听器或挂钩等客户端操作。
先决条件
为了演示服务器组件和服务器操作的功能,我们将使用 Next.js 和 Prisma。
Next.js 是一个用于构建全栈 Web 应用程序的 React 框架。您可以使用 React Components 来构建用户界面,并使用 Next.js 来实现附加功能和优化。在底层,Next.js 还抽象并自动配置 React 所需的工具,例如捆绑、编译等。这使您可以专注于构建应用程序,而不是花时间进行配置。了解更多
Prisma 是一种简化数据库访问和操作的 ORM,让您无需编写 SQL 即可查询和操作数据。了解更多
初始设置
首先创建一个新的 Next.js 应用程序:
纱线创建下一个应用程序服务器示例
您的初始文件夹结构将如下所示:
升级到 Canary 版本以访问 React 19 功能,包括服务器操作:
yarn add next@rc react@rc react-dom@rc
安装 Prisma
yarn add prisma
Prisma 配置
在 src/lib/prisma/schema.prisma 创建 Prisma 模式文件:
generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = "file:./dev.db" } model User { id Int @id @default(autoincrement()) email String @unique name String? age Int }
出于演示目的,我们使用 SQLite。对于生产,您应该使用更强大的数据库。
接下来,在 src/lib/prisma/prisma.ts 添加 Prisma 客户端文件
// ts-ignore 7017 is used to ignore the error that the global object is not // defined in the global scope. This is because the global object is only // defined in the global scope in Node.js and not in the browser. import { PrismaClient } from '@prisma/client' // PrismaClient is attached to the `global` object in development to prevent // exhausting your database connection limit. // // Learn more: // https://pris.ly/d/help/next-js-best-practices const globalForPrisma = global as unknown as { prisma: PrismaClient } export const prisma = globalForPrisma.prisma || new PrismaClient() if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma export default prisma
在package.json中配置Prisma:
{ //other settings "prisma": { "schema": "src/lib/prisma/schema.prisma", "seed": "ts-node src/lib/prisma/seed.ts" } }
并更新 tsconfig.json 中的 TypeScript 设置:
{ //Other settings here... "ts-node": { // these options are overrides used only by ts-node // same as the --compilerOptions flag and the // TS_NODE_COMPILER_OPTIONS environment variable "compilerOptions": { "module": "commonjs" } } }
全局安装 ts-node:
yarn global add ts-node
播种初始数据
在 src/lib/prisma/seed.ts 添加种子文件以填充初始数据:
import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { await prisma.user.create({ email: "anto@prisma.io", name: "Anto", age: 35, }); await prisma.user.create({ email: "vinish@prisma.io", name: "Vinish", age: 32, }); } main() .then(async () => { await prisma.$disconnect(); }) .catch(async (e) => { console.error(e); await prisma.$disconnect(); process.exit(1); });
安装 Prisma 客户端
yarn add @prisma/client
运行迁移命令:
yarn prisma migrate dev --name init
如果种子数据没有反映,请手动添加:
yarn prisma db seed
太棒了!由于安装已准备就绪,您可以创建一个执行数据库操作的操作文件。
创建服务器操作
服务器操作是一项强大的功能,可实现无缝的客户端-服务器相互通信。让我们在 src/actions/user.ts 创建一个用于数据库操作的文件:
"use server"; import prisma from '@/lib/prisma/prisma' import { revalidatePath } from "next/cache"; // export type for user export type User = { id: number; name: string | null; email: string; age: number; }; export async function createUser(user: any) { const resp = await prisma.user.create({ data: user }); console.log("server Response"); revalidatePath("/"); return resp; } export async function getUsers() { return await prisma.user.findMany(); } export async function deleteUser(id: number) { await prisma.user.delete({ where: { id: id, }, }); revalidatePath("/"); }
实施服务器组件
让我们创建一个 React 服务器组件来读取和渲染数据库中的数据。创建 src/app/serverexample/page.tsx:
import UserList from "./Users"; import "./App.css" export default async function ServerPage() { return ( <div classname="app"> <header classname="App-header"> <userlist></userlist> </header> </div> ); }
在 src/app/serverexample/App.css 添加一些样式
.App { text-align: center; } .App-logo { height: 40vmin; pointer-events: none; } .App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } input { color: #000; } .App-link { color: #61dafb; }
创建组件来获取和渲染用户列表:
src/app/serverexample/UserList.tsx
import { getUsers } from "@/actions/user"; import { UserDetail } from "./UserDetail"; export default async function UserList() { //Api call to fetch User details const users = await getUsers(); return ( <div classname="grid grid-cols-3 gap-5"> {users.length ? ( users.map((user) => <userdetail user="{user}"></userdetail>) ) : ( <div classname="col-span3 opacity-60 text-sm text-center"> No User found </div> )} </div> ); }
src/app/serverexample/UserDetail.tsx
export function UserDetail({ user }) { return ( <div classname="flex items-center gap-4 border border-gray-600 py-1 px-4"> <img src="/static/imghw/default1.png" data-src="https://api.dicebear.com/9.x/personas/svg?seed=Shadow" class="lazy" classname="w-10 h-10 rounded-full" alt="React - 服务器操作"> <div classname="font-medium text-base dark:text-white"> <div>{user.name}</div> <div classname="text-sm text-gray-500 dark:text-gray-400"> {user.email} </div> </div> </div> ); }
运行开发服务器:
yarn dev
导航到 http://localhost:3000/serverexample 查看渲染的用户列表:
默认情况下,Next.js 中的组件是服务器组件,除非您指定“使用客户端”指令。注意两点:
- 异步组件定义:服务器组件可以是异步的,因为它们不会重新渲染并且只生成一次。
- 数据获取:行 const users = wait getUsers();从服务器获取数据并在运行时渲染它。
探索服务器操作
服务器操作可实现无缝的客户端-服务器相互通信。让我们添加一个表单来创建新用户。
在 src/app/serverexample/AddUser.tsx 创建一个新文件:
"use client"; import "./app.css"; import { useActionState } from "react"; import { createUser } from "../../actions/user"; const initialState = { error: undefined, }; export default function AddUser() { const submitHandler = async (_previousState: object, formData: FormData) => { try { // This is the Server Action method that transfers the control // Back to the server to do DB operations and get back the result. const response = await createUser({ name: formData.get("name") as string, email: formData.get("email") as string, age: parseInt(formData.get("age") as string), }); return { response }; } catch (error) { return { error }; } }; const [state, submitAction, isPending] = useActionState( submitHandler, initialState ); return ( <div classname="mt-10"> <h4 id="Add-new-User">Add new User</h4>{" "} <form action="%7BsubmitAction%7D" classname="text-base"> <div classname="mt-6 text-right"> Name:{" "} <input classname="ml-2" required name="name" type="text" placeholder="Name"> </div> <div classname="mt-6 text-right"> Email:{" "} <input classname="ml-2" name="email" type="email" placeholder="Email"> </div> <div classname="mt-6 text-right"> Age:{" "} <input classname="ml-2" name="age" type="text" placeholder="Age"> </div> <div classname="mt-6 text-right"> <button disabled classname="bg-green-600 text-white px-5 py-1 text-base disabled:opacity-30"> {isPending ? "Adding" : "Add User"} </button> </div> {(state?.error as string) && <p>{state.error as string}</p>} </form> </div> ); }
更新 src/app/serverexample/page.tsx 以包含 AddUser 组件:
import UserList from "./UserList"; // Import new line import AddUser from "./AddUser"; import "./App.css" export default async function ServerPage() { return ( <div classname="app"> <header classname="App-header"> <userlist></userlist> {/* insert Add User here */} <adduser></adduser> </header> </div> ); }
运行应用程序现在将允许您通过表单添加新用户,并无缝处理服务器端处理。
The AddUser Component and Seamless Client-Server Interaction
The AddUser component is at the heart of this example, showcasing how React Server Actions can revolutionize the way we handle client-server interactions. This component renders a form for adding new users and leverages the useActionState hook to create a smooth and seamless bridge between the client-side interface and server-side operations.
How It Works
- Form Rendering and Data Handling:
- The AddUser component provides a form where users can input their name, email, and age.
- Upon form submission, the data is captured and prepared to be sent to the server.
- useActionState Hook:
- The useActionState hook is a crucial part of this setup. It simplifies the complexity of managing client-side state and server-side actions by abstracting them into a unified interface.
- This hook accepts an asynchronous handler function, which processes the form data and then calls a Server Action method.
- The brilliance of this approach lies in its abstraction: it feels as though you’re invoking a regular function within the same file, even though it actually triggers a server-side operation.
- Server Action Method:
- The createUser function, defined as a Server Action, executes on the server side. It takes the user data from the form, performs the necessary database operations via Prisma, and returns the result.
- This server-side method is crucial for maintaining a clean separation between the client and server, while still enabling them to communicate effectively.
- Seamless Integration:
From the perspective of a developer working on the client side, it appears as if the form submission is handled locally. However, the heavy lifting such as database manipulation occurs on the server.
The useActionState hook encapsulates this process, managing the state transitions and handling errors, while maintaining an intuitive API for developers.
Server Actions Without Forms
So that's with forms, now lets test an example without forms.
update src/app/serverexample/UserDetail.tsx
"use client"; import { deleteUser } from "@/actions/user"; import { useTransition } from "react"; export function UserDetail({ user }) { const [pending, startTransition] = useTransition(); const handleDelete = () => { startTransition(() => { deleteUser(user.id); }); }; return ( <div classname="flex items-center gap-4 border border-gray-600 py-1 px-4"> {pending ? ( <p>Deleting...</p> ) : ( <img src="/static/imghw/default1.png" data-src="https://api.dicebear.com/9.x/personas/svg?seed=Shadow" class="lazy" classname="w-10 h-10 rounded-full" alt="React - 服务器操作"> <div classname="font-medium text-base dark:text-white"> <div>{user.name}</div> <div classname="text-sm text-gray-500 dark:text-gray-400"> {user.email} </div> </div> <button classname="ml-auto" onclick="{handleDelete}"> <img src="/static/imghw/default1.png" data-src="/delete.png" class="lazy" classname="w-4 h-4" alt=""> </button> > )} </div> ); }
Key Points:
- Server Action: deleteUser(user.id) is a server action that removes the user from the database. This operation is triggered without any form submission.
- useTransition: This hook allows you to manage the asynchronous state of the deletion process, showing a "Deleting..." message while the operation is in progress.
- User Interface: The component maintains a clean UI, dynamically updating based on the action status.
Now, you can seamlessly delete a user within the application:
Conclusion
This approach is transformative because it abstracts away the complexities of client-server communication. Traditionally, such interactions would require handling API endpoints, managing asynchronous requests, and carefully coordinating client-side state with server responses. With React Server Actions and the useActionState hook, this complexity is reduced, allowing developers to focus more on building features rather than worrying about the underlying infrastructure.
By using this pattern, you gain:
- Cleaner Code: The client-side code remains simple and focused, without the need for explicit API calls.
- Improved Developer Experience: Server-side operations are seamlessly integrated, reducing cognitive load and potential for errors.
- Enhanced Performance: Server Actions are optimized for performance, reducing unnecessary client-server round trips and ensuring that server-side resources are used efficiently.
You can find the full code in the repository
以上是React - 服务器操作的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。
