useFormState - This feature is available in the latest Canary

Canary

ה-Hook useFormState זמין כרגע רק בערוצי Canary ו-experimental של React. מידע נוסף ב-ערוצי שחרור. בנוסף, צריך להשתמש ב-framework שתומך ב-React Server Components כדי לקבל את מלוא התועלת מ-useFormState.

useFormState הוא Hook שמאפשר לעדכן state על בסיס תוצאת פעולה של טופס.

const [state, formAction] = useFormState(fn, initialState, permalink?);

Reference

useFormState(action, initialState, permalink?)

קראו ל-useFormState ברמה העליונה של הקומפוננטה כדי ליצור state לקומפוננטה שמתעדכן כשפעולת טופס מופעלת. אתם מעבירים ל-useFormState פונקציית form action קיימת יחד עם state התחלתי, והפונקציה מחזירה action חדשה שתשתמשו בה בטופס, יחד עם מצב הטופס העדכני ביותר. מצב הטופס העדכני גם מועבר לפונקציה שסיפקתם.

import { useFormState } from "react-dom";

async function increment(previousState, formData) {
return previousState + 1;
}

function StatefulForm({}) {
const [state, formAction] = useFormState(increment, 0);
return (
<form>
{state}
<button formAction={formAction}>Increment</button>
</form>
)
}

מצב הטופס הוא הערך שמוחזר מה-action כשהטופס הוגש לאחרונה. אם הטופס עדיין לא הוגש, זה יהיה ה-state ההתחלתי שהעברתם.

בשימוש עם Server Action, ‏useFormState מאפשר להציג את תגובת השרת מהגשת הטופס עוד לפני שה-hydration הושלם.

ראו דוגמאות נוספות בהמשך.

Parameters

  • fn: הפונקציה שתיקרא כששולחים את הטופס או לוחצים על כפתור. כשהפונקציה נקראת, היא תקבל את מצב הטופס הקודם (בהתחלה ה-initialState שהעברתם, ובהמשך ערך ההחזרה הקודם שלה) כארגומנט ראשון, ואז את הארגומנטים ש-form action מקבלת בדרך כלל.
  • initialState: הערך שתרצו שיהיה למצב בתחילה. הוא יכול להיות כל ערך שניתן לסריאליזציה. מתעלמים מהארגומנט הזה אחרי הפעלת הפעולה בפעם הראשונה.
  • אופציונלי permalink: מחרוזת שמכילה את URL העמוד הייחודי שהטופס הזה משנה. מיועד לעמודים עם תוכן דינמי (למשל feed) יחד עם progressive enhancement: אם fn היא server action והטופס נשלח לפני ש-JavaScript bundle נטען, הדפדפן ינווט ל-URL של ה-permalink שצוין במקום ל-URL של העמוד הנוכחי. ודאו שאותה קומפוננטת טופס מרונדרת בעמוד היעד (כולל אותה action fn ואותו permalink) כדי ש-React תדע להעביר את המצב. אחרי שהטופס עובר hydration, לפרמטר הזה אין השפעה.

Returns

useFormState מחזירה מערך עם שני ערכים בדיוק:

  1. המצב הנוכחי. בזמן הרינדור הראשון הוא יתאים ל-initialState שהעברתם. אחרי שהפעולה מופעלת, הוא יתאים לערך שהוחזר מהפעולה.
  2. פעולה חדשה שאפשר להעביר כ-prop בשם action לקומפוננטת form שלכם, או כ-prop בשם formAction לכל קומפוננטת button בתוך הטופס.

Caveats

  • כשמשתמשים במסגרת שתומכת ב-React Server Components, ‏useFormState מאפשרת להפוך טפסים לאינטראקטיביים עוד לפני ש-JavaScript בוצע בלקוח. בשימוש בלי Server Components, זה שקול ל-state מקומי של קומפוננטה.
  • הפונקציה שמועברת ל-useFormState מקבלת ארגומנט נוסף — ה-state הקודם או ההתחלתי — כארגומנט ראשון. לכן ה-signature שלה שונה מאשר שימוש ישיר בה כ-form action בלי useFormState.

Usage

שימוש במידע שמוחזר מפעולת טופס

קראו ל-useFormState ברמה העליונה של הקומפוננטה כדי לגשת לערך ההחזרה של action מהפעם האחרונה שהטופס הוגש.

import { useFormState } from 'react-dom';
import { action } from './actions.js';

function MyComponent() {
const [state, formAction] = useFormState(action, null);
// ...
return (
<form action={formAction}>
{/* ... */}
</form>
);
}

useFormState מחזירה מערך עם שני פריטים בדיוק:

  1. ה-state הנוכחי של הטופס, שמוגדר בתחילה ל-state ההתחלתי שסיפקתם, ואחרי הגשת הטופס מוגדר לערך ההחזרה של ה-action שסיפקתם.
  2. action חדשה שאתם מעבירים ל-<form> כ-prop בשם action.

כשהטופס נשלח, פונקציית ה-action שסיפקתם תיקרא. ערך ההחזרה שלה יהפוך ל-state הנוכחי החדש של הטופס.

ה-action שסיפקתם תקבל גם ארגומנט ראשון חדש: ה-state הנוכחי של הטופס. בפעם הראשונה שהטופס נשלח, זה יהיה ה-state ההתחלתי שסיפקתם; בשליחות הבאות, זה יהיה ערך ההחזרה מהפעם הקודמת שהפעולה נקראה. שאר הארגומנטים זהים למצב שבו useFormState לא הייתה בשימוש.

function action(currentState, formData) {
// ...
return 'next state';
}

Display information after submitting a form

Example 1 of 2:
הצגת שגיאות טופס

כדי להציג הודעות כמו שגיאה או toast שמוחזרות מ-Server Action, עטפו את הפעולה בקריאה ל-useFormState.

import { useState } from "react";
import { useFormState } from "react-dom";
import { addToCart } from "./actions.js";

function AddToCartForm({itemID, itemTitle}) {
  const [message, formAction] = useFormState(addToCart, null);
  return (
    <form action={formAction}>
      <h2>{itemTitle}</h2>
      <input type="hidden" name="itemID" value={itemID} />
      <button type="submit">Add to Cart</button>
      {message}
    </form>
  );
}

export default function App() {
  return (
    <>
      <AddToCartForm itemID="1" itemTitle="JavaScript: The Definitive Guide" />
      <AddToCartForm itemID="2" itemTitle="JavaScript: The Good Parts" />
    </>
  )
}

Troubleshooting

הפעולה שלי כבר לא יכולה לקרוא את נתוני הטופס שנשלחו

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

function action(currentState, formData) {
// ...
}