'use server' - This feature is available in the latest Canary

Canary

'use server' נדרש רק אם אתם משתמשים ב-React Server Components או בונים ספרייה שתואמת אליהם.

'use server' מסמן פונקציות בצד השרת שאפשר לקרוא להן מקוד בצד הלקוח.


עיון ב-API

'use server'

הוסיפו 'use server' בתחילת גוף של פונקציה אסינכרונית כדי לסמן אותה כפונקציה שהלקוח יכול לקרוא לה. לפונקציות האלו אנחנו קוראים Server Actions.

async function addToCart(data) {
'use server';
// ...
}

כשקוראים ל-Server Action מהלקוח, מתבצעת בקשת רשת לשרת שכוללת עותק מסודר (serialized) של כל הארגומנטים שהועברו. אם ה-Server Action מחזיר ערך, הערך הזה יעבור serialization ויוחזר ללקוח.

במקום לסמן כל פונקציה בנפרד עם 'use server', אפשר להוסיף את ההנחיה בראש קובץ כדי לסמן שכל ה-exports בקובץ הם Server Actions שאפשר להשתמש בהם מכל מקום, כולל ייבוא מתוך קוד לקוח.

הבהרות

  • 'use server' חייב להופיע ממש בתחילת הפונקציה או המודול, לפני כל קוד אחר כולל imports (מותרות הערות מעל ההנחיה). יש לכתוב אותו במרכאות יחידות או כפולות, לא עם backticks.
  • אפשר להשתמש ב-'use server' רק בקבצים שרצים בצד השרת. את ה-Server Actions שנוצרים אפשר להעביר ל-Client Components דרך props. ראו types נתמכים ל-serialization.
  • כדי לייבא Server Action מתוך קוד לקוח, ההנחיה חייבת להיות ברמת מודול.
  • מכיוון שקריאות הרשת מתחת למכסה המנוע הן תמיד אסינכרוניות, אפשר להשתמש ב-'use server' רק על פונקציות async.
  • התייחסו תמיד לארגומנטים של Server Actions כקלט לא מהימן ובצעו הרשאה לכל שינוי מצב. ראו שיקולי אבטחה.
  • מומלץ לקרוא ל-Server Actions בתוך transition. Server Actions שמועברים ל-<form action> או ל-formAction ירוצו אוטומטית בתוך transition.
  • Server Actions מיועדים למוטציות שמעדכנות מצב בצד השרת; הם לא מומלצים לשליפת נתונים. בהתאם לכך, frameworks שמממשים Server Actions לרוב מעבדים פעולה אחת בכל פעם ואין להם דרך למטמן את ערך החזרה.

שיקולי אבטחה

הארגומנטים ל-Server Actions נשלטים לגמרי על ידי הלקוח. מטעמי אבטחה, התייחסו אליהם תמיד כקלט לא מהימן, ודאו שאתם מאמתים ומבצעים escaping לארגומנטים לפי הצורך.

בכל Server Action, ודאו שהמשתמש המחובר מורשה לבצע את הפעולה.

Under Construction

כדי למנוע שליחה של מידע רגיש מתוך Server Action, קיימים APIs ניסיוניים מסוג taint שמונעים העברת ערכים ייחודיים ואובייקטים לקוד לקוח.

ראו experimental_taintUniqueValue ו-experimental_taintObjectReference.

ארגומנטים וערכי החזרה שניתנים ל-serialization

מכיוון שקוד לקוח קורא ל-Server Action דרך הרשת, כל הארגומנטים שמועברים חייבים להיות ניתנים ל-serialization.

אלה הסוגים הנתמכים לארגומנטים של Server Action:

לעומת זאת, אלה אינם נתמכים:

  • React elements או JSX
  • פונקציות, כולל פונקציות קומפוננטה או כל פונקציה אחרת שאינה Server Action
  • Classes
  • אובייקטים שהם מופעים של כל class (מלבד ה-built-ins שהוזכרו) או אובייקטים עם null prototype
  • Symbols שלא נרשמו גלובלית, לדוגמה: Symbol('my new symbol')

ערכי חזרה נתמכים ב-serialization זהים ל-serializable props עבור Client Component בגבול.

שימוש

Server Actions בתוך טפסים

מקרה השימוש הנפוץ ביותר ל-Server Actions הוא קריאה לפונקציות שרת שמבצעות מוטציה בנתונים. בדפדפן, אלמנט HTML form הוא הדרך המסורתית לשלוח מוטציה מצד המשתמש. עם React Server Components, React מוסיף תמיכה מדרגה ראשונה ב-Server Actions בתוך forms.

הנה טופס שמאפשר למשתמש לבקש שם משתמש.

// App.js

async function requestUsername(formData) {
'use server';
const username = formData.get('username');
// ...
}

export default function App() {
return (
<form action={requestUsername}>
<input type="text" name="username" />
<button type="submit">Request</button>
</form>
);
}

בדוגמה הזו requestUsername הוא Server Action שמועבר ל-<form>. כשמשתמש שולח את הטופס, מתבצעת בקשת רשת לפונקציית השרת requestUsername. כשקוראים ל-Server Action מתוך טופס, React יעביר את FormData של הטופס כארגומנט ראשון ל-Server Action.

על ידי העברת Server Action ל-action של הטופס, React יכול לבצע progressive enhancement לטופס. המשמעות היא שאפשר לשלוח טפסים גם לפני שחבילת JavaScript נטענה.

טיפול בערכי חזרה בטפסים

בטופס בקשת שם משתמש ייתכן שהשם לא זמין. requestUsername צריך להחזיר לנו אם הפעולה הצליחה או נכשלה.

כדי לעדכן את ה-UI לפי תוצאת Server Action תוך תמיכה ב-progressive enhancement, השתמשו ב-useFormState.

// requestUsername.js
'use server';

export default async function requestUsername(formData) {
const username = formData.get('username');
if (canRequest(username)) {
// ...
return 'successful';
}
return 'failed';
}
// UsernameForm.js
'use client';

import { useFormState } from 'react-dom';
import requestUsername from './requestUsername';

function UsernameForm() {
const [returnValue, action] = useFormState(requestUsername, 'n/a');

return (
<>
<form action={action}>
<input type="text" name="username" />
<button type="submit">Request</button>
</form>
<p>Last submission request returned: {returnValue}</p>
</>
);
}

שימו לב שכמו רוב ה-Hooks, useFormState יכול להיקרא רק מתוך קוד לקוח.

קריאה ל-Server Action מחוץ ל-<form>

Server Actions הם endpoints בשרת, ואפשר לקרוא להם מכל מקום בקוד לקוח.

כשמשתמשים ב-Server Action מחוץ ל-form, קראו לו בתוך transition, כדי שתוכלו להציג אינדיקציית טעינה, להראות optimistic state updates, ולטפל בשגיאות לא צפויות. טפסים עוטפים Server Actions אוטומטית בתוך transitions.

import incrementLike from './actions';
import { useState, useTransition } from 'react';

function LikeButton() {
const [isPending, startTransition] = useTransition();
const [likeCount, setLikeCount] = useState(0);

const onClick = () => {
startTransition(async () => {
const currentCount = await incrementLike();
setLikeCount(currentCount);
});
};

return (
<>
<p>Total Likes: {likeCount}</p>
<button onClick={onClick} disabled={isPending}>Like</button>;
</>
);
}
// actions.js
'use server';

let likeCount = 0;
export default async function incrementLike() {
likeCount++;
return likeCount;
}

כדי לקרוא את ערך החזרה של Server Action צריך לבצע await ל-promise שמוחזר.