useActionState خطاف في React 19 يشغّل دالة غير متزامنة (إجراءً)، ويخزّن ما تُرجعه كحالة، ويخبرك ما إذا كانت ما زالت تعمل. تستدعيه على شكل const [state, formAction, isPending] = useActionState(action, initialState) وتمرر formAction إلى <form>.
أرسل النموذج فارغًا أولًا: يُرجع الإجراء كائن خطأ وتظهر الرسالة. ثم اكتب عنوانًا فيه @ وأرسل مرة أخرى. لثانية يقرأ الزر "Subscribing..." ويكون معطّلًا.
الصيغة
const [state, formAction, isPending] = useActionState(action, initialState, permalink?);
actionهي الدالة التي تعمل. تستدعيها React على شكلaction(previousState, ...args). وما تُرجعه (أو ما تُحل إليه، إذا كانت غير متزامنة) يصبحstateالجديدة.initialStateهي الحالة قبل أن يعمل الإجراء أبدًا. استخدم الشكل نفسه الذي يُرجعه الإجراء، ليتمكن المكوّن من قراءةstate.messageفي العرض الأول أيضًا.stateهي أحدث نتيجة.formActionنسخة مغلّفة من إجرائك. مررها إلى<form action>أو<button formAction>، أو استدعِها بنفسك.- تكون
isPendingقيمتهاtrueمن لحظة بدء الإجراء حتى ينتهي. permalinkاختيارية ولا تهم إلا لدوال الخادم في إطار عمل: عنوان URL الذي ينبغي أن يذهب إليه النموذج إذا أُرسل قبل تحميل JavaScript.
يستقبل الإجراء الحالة السابقة كوسيطه الأول. هذا أشيع خطأ عند نقل نموذج: كتابة async function save(formData) تعطيك الحالة القديمة في formData، فيرمي formData.get خطأ.
قراءة النموذج بـ FormData
عندما تُستخدم formAction على أنها action النموذج، تستدعي React إجراءك مع FormData الخاص بالنموذج. كل حقل له name موجود فيه. لا تحتاج إلى useState للحقول ولا إلى معالج onChange، ولا تستدعي e.preventDefault(): تتولى React حدث الإرسال.
ألغِ تحديد Spicy وأرسل مرة أخرى: يختفي المفتاح spicy من البيانات المسجّلة، لأن مربع الاختيار غير المحدد لا يرسل شيئًا. ويقرأ الإجراء أيضًا previousState.count ليضيف واحدًا، ولهذا تأتي الحالة السابقة أولًا.
إرجاع أخطاء التحقق
مكان التحقق هو الإجراء. أرجع الأخطاء كجزء من الحالة، وأرجع القيم التي كتبها المستخدم ليتمكن النموذج من إعادتها. بعد انتهاء إجراء النموذج، تعيد React ضبط الحقول غير المتحكم بها، فالحقل الذي ليس له defaultValue={state.values...} سيعود فارغًا بعد إرسال فاشل.
اكتب اسمًا من حرف واحد وكلمة مرور قصيرة، ثم أرسل. يظهر الخطآن ويحتفظ الحقلان بما كتبته. احذف defaultValue={state.values.name} وأرسل مرة أخرى: يفرّغ حقل الاسم نفسه بعد الخطأ.
في تطبيق حقيقي أبقِ الخادم هو الحكم النهائي. تعطي فحوص العميل ملاحظات سريعة، لكن أي شخص يستطيع إرسال طلب دون نموذجك.
استدعاء الإجراء خارج نموذج
formAction دالة، فيستطيع زر أو تأثير استدعاءها أيضًا. عندما تستدعيها بنفسك، غلّف الاستدعاء في startTransition. يفعل النموذج ذلك نيابة عنك؛ أما onClick العادي فلا، ومن دون انتقال لا تتحدث isPending بشكل صحيح (تسجّل React خطأ بشأن ذلك في التطوير).
انقر Load more بضع مرات. كل استدعاء يبدأ من الحالة التي أرجعها السابق، فتُلحق الصفحات بالترتيب. وإذا استدعيت الإجراء مرة أخرى وهو ما زال يعمل، تضع React الاستدعاء في طابور وتشغّله بعد الحالي، كل منها بأحدث حالة.
من أين جاء useActionState
قبل صدور React 19، كان هذا الخطاف متاحًا في نسخ canary باسم useFormState، مستوردًا من react-dom. ثم أُعيدت تسميته إلى useActionState، ونُقل إلى react، وأُعطي القيمة isPending. إذا وجدت كودًا قديمًا مثل هذا، فالإصلاح هو الاستيراد والاسم:
// 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);
ولعرض حالة انتظار في زر إرسال متداخل بعمق، يقرأ useFormStatus من react-dom حالة النموذج الأب دون props. تسرده نظرة عامة على React 19 مع ميزات النماذج الأخرى.
الأخطاء ومتى تستخدم شيئًا آخر
إذا رمى الإجراء خطأ، تعرض React أقرب حدود أخطاء بدلًا من تحديث الحالة. أما الأخطاء التي يستطيع المستخدم إصلاحها، مثل التحقق أو اسم مستخدم محجوز، فأرجعها كحالة، كما في مثال التسجيل. ودع الأعطال الحقيقية ترمي.
useActionState مخصص لنتيجة إجراء. ولقيمة تتحدث فورًا أثناء عمل الإجراء، مثل عدد إعجابات ينبغي أن يرتفع عند النقر، اجمعه مع useOptimistic. وللإدخال الذي يتغير مع كل ضغطة مفتاح ويُقرأ أثناء العرض، ما زال useState العادي مع حقل متحكم به الأداة المناسبة.
أخطاء شائعة
نسيان معامل الحالة السابقة. تستقبل async function save(formData) الحالة السابقة في formData. التوقيع في النموذج دائمًا (previousState, formData).
حقول بلا name. لا يحتوي FormData إلا على الحقول التي لها سمة name. الحقل الذي ليس له إلا id غائب عن formData.get(...)، فتُرجع null.
حقول متحكم بها في كل مكان. ما زال بإمكانك استخدام value وonChange داخل إجراء نموذج، لكنك لست مضطرًا. الحقول غير المتحكم بها مع name وdefaultValue أبسط توافقًا مع الإجراءات، وإعادة الضبط التي تجريها React بعد الإرسال لا تؤثر إلا في الحقول غير المتحكم بها.
قراءة state داخل الإجراء. قيمة state من المكوّن هي القيمة من العرض الذي أنشأ المعالج. داخل الإجراء استخدم previousState، التي تكون دائمًا نتيجة الاستدعاء الذي قبله، حتى عندما توضع عدة استدعاءات في طابور.
رمي أخطاء للحالات المتوقعة. الخطأ المرمي يستبدل النموذج بأقرب حدود أخطاء. كلمة المرور الخاطئة ليست انهيارًا؛ أرجعها كحالة.
الأسئلة الشائعة
ماذا يُرجع useActionState؟
مصفوفة من ثلاثة عناصر: الحالة الحالية (القيمة التي أرجعها الإجراء آخر مرة، أو الحالة الابتدائية قبل أن يعمل)، وإجراء جديد تمرره إلى نموذج أو تستدعيه بنفسك، وisPending، التي تكون true ما دام الإجراء يعمل.
ما الوسائط التي يستقبلها الإجراء؟
الحالة السابقة أولًا، ثم ما استُدعي به الإجراء. في <form action={formAction}> يكون ذلك الوسيط الثاني FormData الخاص بالنموذج.
ما الفرق بين useActionState وuseFormState؟
هما الخطاف نفسه. صدر في نسخ React التجريبية (canary) باسم useFormState في react-dom، ثم أُعيدت تسميته إلى useActionState ونُقل إلى react في React 19، واكتسب القيمة isPending في الوقت نفسه.
هل يمكنني استخدام useActionState دون نموذج؟
نعم. استدعِ الإجراء المُرجع بنفسك، داخل startTransition، مثلًا من onClick الخاص بزر. ومن دون انتقال لا تتحدث isPending بشكل صحيح وتسجّل React خطأ في التطوير.
لماذا يُفرَّغ نموذجي بعد الإرسال؟
بعد انتهاء إجراء النموذج، تعيد React ضبط الحقول غير المتحكم بها في النموذج، كما يفعل المتصفح بعد إرسال عادي. وللاحتفاظ بقيمة بعد خطأ، أرجعها في الحالة ومررها إلى defaultValue الخاصة بالحقل.