首頁  >  文章  >  web前端  >  React - 伺服器操作

React - 伺服器操作

WBOY
WBOY原創
2024-08-09 07:34:12951瀏覽

反應表單動作。

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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
上一篇:服務人員下一篇:服務人員