首页 > web前端 > js教程 > 正文

React - 服务器操作

WBOY
发布: 2024-08-09 07:34:12
原创
988 人浏览过

反应表单动作。

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 应用程序:
纱线创建下一个应用程序服务器示例

您的初始文件夹结构将如下所示:

React - Server Actions

升级到 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 classname="w-10 h-10 rounded-full" src="https://api.dicebear.com/9.x/personas/svg?seed=Shadow" 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 查看渲染的用户列表:
React - Server Actions

默认情况下,Next.js 中的组件是服务器组件,除非您指定“使用客户端”指令。注意两点:

  1. 异步组件定义:服务器组件可以是异步的,因为它们不会重新渲染并且只生成一次。
  2. 数据获取:行 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 classname="text-center">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>
  );
}
登录后复制

运行应用程序现在将允许您通过表单添加新用户,并无缝处理服务器端处理。
React - Server Actions

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

  1. 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.
  1. 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.
  1. 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.
  1. 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 classname="w-10 h-10 rounded-full" src="https://api.dicebear.com/9.x/personas/svg?seed=Shadow" 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 classname="w-4 h-4" src="/delete.png" 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:
React - Server Actions

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中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!