Menu

ההוק useActionState ב-React: טפסים, שגיאות ו-pending

useActionState עוטף action אסינכרוני, שומר את הערך שהוא החזיר לאחרונה, ואומר לכם אם הוא עדיין רץ. למדו את החתימה, form actions, החזרת שגיאות ולידציה, וקריאה לו מחוץ לטופס.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

useActionState הוא הוק של React 19 שמריץ פונקציה async (action), שומר את מה שהיא מחזירה כ-state, ואומר לכם אם היא עדיין רצה. קוראים לו בתור const [state, formAction, isPending] = useActionState(action, initialState) ומעבירים את formAction ל-<form>.

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

התחביר

const [state, formAction, isPending] = useActionState(action, initialState, permalink?);
  • action היא הפונקציה להרצה. React קוראת לה בתור action(previousState, ...args). מה שהיא מחזירה (או מסתיימת איתו, אם היא async) הופך ל-state החדש.
  • initialState הוא ה-state לפני שה-action רץ אי פעם. השתמשו באותה צורה שה-action מחזיר, כדי שהקומפוננטה תוכל לקרוא את state.message גם ברינדור הראשון.
  • state הוא התוצאה האחרונה.
  • formAction היא גרסה עטופה של ה-action שלכם. העבירו אותה ל-<form action> או ל-<button formAction>, או קראו לה בעצמכם.
  • isPending הוא true מהרגע שה-action מתחיל ועד שהוא מסתיים.
  • permalink אופציונלי ורלוונטי רק ל-Server Functions ב-framework: הכתובת שהטופס צריך לעבור אליה אם הוא נשלח לפני ש-JavaScript נטען.

ה-action מקבל את ה-state הקודם כארגומנט הראשון שלו. זו הטעות הנפוצה ביותר כשמעבירים טופס: כתיבת async function save(formData) נותנת לכם את ה-state הישן ב-formData, ו-formData.get זורק שגיאה.

קריאת הטופס עם FormData

כש-formAction משמש כ-action של טופס, React קוראת ל-action שלכם עם ה-FormData של הטופס. כל שדה עם name נמצא בו. לא צריך useState לשדות או handler של onChange, ולא קוראים ל-e.preventDefault(): React מטפלת באירוע השליחה.

בטלו את הסימון ב-Spicy ושלחו שוב: המפתח spicy נעלם מהנתונים שנרשמו, כי checkbox לא מסומן לא שולח כלום. ה-action גם קורא את previousState.count כדי להוסיף אחד, וזו הסיבה שה-state הקודם בא ראשון.

החזרת שגיאות ולידציה

ולידציה שייכת ל-action. החזירו את השגיאות כחלק מה-state, והחזירו את הערכים שהמשתמש הקליד כדי שהטופס יוכל להחזיר אותם למקום. אחרי ש-form action מסתיים, React מאפסת את השדות הלא מבוקרים של הטופס, כך ששדה בלי defaultValue={state.values...} היה חוזר ריק אחרי שליחה שנכשלה.

הקלידו שם של אות אחת וסיסמה קצרה, ואז שלחו. שתי השגיאות מופיעות ושני השדות שומרים את מה שהקלדתם. הסירו את defaultValue={state.values.name} ושלחו שוב: שדה השם מתרוקן אחרי השגיאה.

באפליקציה אמיתית השאירו לשרת את המילה האחרונה. בדיקות בצד הלקוח נותנות משוב מהיר, אבל כל אחד יכול לשלוח בקשה בלי הטופס שלכם.

קריאה ל-action מחוץ לטופס

formAction היא פונקציה, ולכן גם כפתור או אפקט יכולים לקרוא לה. כשקוראים לה בעצמכם, עטפו את הקריאה ב-startTransition. טופס עושה את זה בשבילכם; onClick רגיל לא, ובלי transition isPending לא מתעדכן נכון (React רושמת על זה שגיאה בפיתוח).

לחצו על Load more כמה פעמים. כל קריאה מתחילה מה-state שהקודמת החזירה, כך שדפים מתווספים לפי הסדר. אם קוראים ל-action שוב בזמן שהוא עדיין רץ, React מכניסה את הקריאה לתור ומריצה אותה אחרי הנוכחית, כל אחת עם ה-state העדכני.

מאיפה useActionState הגיע

לפני ש-React 19 שוחררה, ההוק הזה היה זמין בגרסאות canary בתור useFormState, שמיובא מ-react-dom. השם שלו שונה ל-useActionState, הוא עבר ל-react, וקיבל את הערך isPending. אם אתם מוצאים קוד ישן כזה, התיקון הוא ה-import והשם:

// React canary, before 19
import { useFormState } from 'react-dom';
const [state, formAction] = useFormState(action, initialState);

// React 19
import { useActionState } from 'react';
const [state, formAction, isPending] = useActionState(action, initialState);

כדי להציג מצב pending בכפתור שליחה שמקונן עמוק, useFormStatus מ-react-dom קורא את הסטטוס של הטופס ההורה בלי props. הסקירה של React 19 מציגה אותו עם שאר יכולות הטפסים.

שגיאות ומתי להשתמש במשהו אחר

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

useActionState נועד לתוצאה של action. לערך שמתעדכן מיד בזמן שה-action רץ, כמו מונה לייקים שצריך לעלות בלחיצה, שלבו אותו עם useOptimistic. לקלט שמשתנה בכל הקשה ונקרא בזמן הרינדור, useState רגיל ושדה מבוקר הם עדיין הכלי הנכון.

טעויות נפוצות

לשכוח את פרמטר ה-state הקודם. async function save(formData) מקבלת את ה-state הקודם ב-formData. בטופס החתימה היא תמיד (previousState, formData).

שדות בלי name. FormData מכיל רק שדות שיש להם מאפיין name. שדה עם id בלבד חסר ב-formData.get(...), שמחזיר null.

שדות מבוקרים בכל מקום. עדיין אפשר להשתמש ב-value וב-onChange בתוך form action, אבל לא חייבים. שדות לא מבוקרים עם name ו-defaultValue הם ההתאמה הפשוטה יותר ל-actions, והאיפוס של React אחרי שליחה משפיע רק על שדות לא מבוקרים.

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

לזרוק שגיאה על שגיאות צפויות. שגיאה שנזרקת מחליפה את הטופס ב-error boundary הקרוב. סיסמה שגויה היא לא קריסה; החזירו אותה כ-state.

שאלות נפוצות

מה useActionState מחזיר?

מערך של שלושה פריטים: ה-state הנוכחי (הערך שה-action החזיר לאחרונה, או ה-state ההתחלתי לפני שהוא רץ), action חדש להעביר לטופס או לקרוא לו בעצמכם, ו-isPending, שהוא true בזמן שה-action רץ.

אילו ארגומנטים ה-action מקבל?

קודם את ה-state הקודם, ואז את מה שה-action נקרא איתו. ב-<form action={formAction}> הארגומנט השני הזה הוא ה-FormData של הטופס.

מה ההבדל בין useActionState ל-useFormState?

זה אותו הוק. הוא יצא בגרסאות canary של React בתור useFormState ב-react-dom, שינה את שמו ל-useActionState ועבר ל-react ב-React 19, ובאותו זמן קיבל את הערך isPending.

אפשר להשתמש ב-useActionState בלי טופס?

כן. קראו בעצמכם ל-action שהוחזר, בתוך startTransition, למשל מ-onClick של כפתור. בלי transition, isPending לא מתעדכן נכון ו-React רושמת שגיאה בפיתוח.

למה הטופס שלי מתנקה אחרי שליחה?

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

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל