use واجهة في React 19 تقرأ قيمة من وعد أو من سياق أثناء عرض المكوّن. يوقف use(promise) المكوّن حتى يُحل الوعد، ويعرض بديل أقرب <Suspense> في الأثناء؛ ويعمل use(context) مثل useContext لكن يمكن أن يكون داخل if أو حلقة.
الدالة fetchUser أدناه بديل عن طلب حقيقي.
يعرض العرض الأول "Loading..." لثانية واحدة، ثم الملف الشخصي. انقر User 2: يعود البديل ما دام الوعد الجديد معلقًا. ليس لـ Profile حالة تحميل خاصة به؛ فهو مكتوب كأن البيانات موجودة أصلًا.
كيف يعمل use(promise)
عندما يستدعي Profile الخطاف use(userPromise):
- إذا كان الوعد قد حُل، يُرجع
useقيمته ويستمر العرض. - وإذا كان ما زال معلقًا، توقف React عرض
Profileوتعرض بديل أقرب<Suspense>فوقه. وعندما يستقر الوعد، تعرض React المكوّنProfileمن جديد، وهذه المرة يُرجعuseالقيمة. - وإذا رُفض، تعرض React أقرب حدود أخطاء بدلًا من ذلك.
تتذكر React النتيجة على كائن الوعد نفسه. ولهذا يجب أن يكون الوعد هو الكائن نفسه في العرض التالي.
ولإبقاء الملف الشخصي القديم على الشاشة أثناء تحميل التالي، اضبط الوعد داخل انتقال. عندها تنتظر React بدلًا من عرض البديل من جديد:
<button onClick={() => startTransition(() => setUserPromise(fetchUser(2)))}>User 2</button>
أين تنشئ الوعد
هذا هو الجزء الذي يوقع الناس في الخطأ. كتابة استدعاء الجلب داخل المكوّن الذي يقرؤه تبدو طبيعية لكنها لا تعمل:
// Do not do this
function Profile({ id }) {
const user = use(fetchUser(id)); // a new promise on every render
return <p>{user.name}</p>;
}
كل عرض يستدعي fetchUser ويحصل على وعد جديد معلق. يُعلَّق Profile. وعندما يُحل ذلك الوعد، تعرض React Profile من جديد، فينشئ وعدًا معلقًا آخر، فيُعلَّق من جديد. لا يعرض المكوّن البيانات أبدًا، ويمتلئ تبويب الشبكة بالطلبات.
أنشئ الوعد في مكان لا يُعاد تشغيله مع كل عرض للقارئ:
- في أب، محفوظًا في الحالة (كما في المثال الأول) أو منشأً في معالج حدث.
- على مستوى الوحدة، عندما لا تعتمد البيانات على props:
const configPromise = fetchConfig();فوق المكوّن. - في ذاكرة مؤقتة مفتاحها الوسائط، فيُرجع المعرّف نفسه الوعد نفسه.
- في إطار عمل: يستطيع مكوّن خادم تمرير وعد إلى مكوّن عميل كـ prop، وتفعل محمّلات المسارات الشيء نفسه. وهذه هي الحالة التي صُمم
useلأجلها.
إليك نسخة الذاكرة المؤقتة، ليتمكن مكوّن من طلب البيانات بالمعرّف مع الحصول على وعد ثابت:
تنقّل بين المدن الثلاث، ثم عد إلى مدينة فتحتها من قبل. تسجّل وحدة التحكم طلبًا واحدًا لكل مدينة، والمدينة التي رأيتها من قبل تظهر دون البديل، لأن وعدها قد حُل أصلًا. احذف الفحص if (!cache.has(city)) فيعود المكوّن إلى التعليق إلى الأبد.
تحتاج الذاكرة المؤقتة الحقيقية أيضًا إلى طريقة لإنهاء صلاحية المدخلات. تتولى مكتبات البيانات وأطر العمل ذلك، ولهذا تحصل معظم التطبيقات على وعودها من إحداها.
use(context)، حتى داخل if
يُرجع use(SomeContext) القيمة نفسها التي يُرجعها useContext(SomeContext). والفرق هو أين يمكنك استدعاؤه. يجب أن يعمل كل خطاف آخر في المستوى الأعلى، وبالترتيب نفسه في كل عرض (قواعد الخطافات). أما use فيمكن استدعاؤه بعد إرجاع مبكر، أو داخل شرط، أو في حلقة.
انقر الزر للتبديل بين الفرعين. استبدل use(UserContext) بـ useContext(UserContext) فيخرق الكود قواعد الخطافات، لأن الخطاف سيعمل في بعض مرات العرض دون غيرها.
ما زالت لـ use حدود: لا تستدعِه إلا أثناء عرض مكوّن أو داخل خطاف مخصص. وفي معالج حدث أو تأثير، اقرأ السياق بقيمة حصلت عليها أصلًا أثناء العرض.
الأخطاء: حدود الأخطاء لا try/catch
عندما يُرفض الوعد، يذهب الخطأ إلى أقرب حدود أخطاء، بالطريقة نفسها التي يذهب بها خطأ العرض.
افتح Post 2 فتعرض الحدود رسالة الخطأ. افتح Post 1 مرة أخرى فيعمل: يعطي key={id} الحدود حالة جديدة لكل منشور، فلا تظل تعرض الخطأ القديم.
لا يمكنك التقاط use معلق أو مرفوض بـ try/catch. ما دام الوعد معلقًا، يقاطع use العرض برمي قيمة خاصة، وكتلة catch حوله ستبتلعها. القيمة الملتقطة خطأ تبدأ رسالته بـ "Suspense Exception: This is not a real error!"، وفي التطوير تسجّل React أيضًا "use was called from inside a try/catch block". ولعرض قيمة احتياطية بدلًا من شاشة خطأ، تعامل مع الرفض على الوعد قبل تمريره إلى الأسفل:
const postPromise = fetchPost(id).catch(() => 'This post could not be loaded.');
use مقارنة بالجلب في useEffect
النمط الكلاسيكي في العميل يجلب في تأثير ويحتفظ بثلاث قطع من الحالة:
function Profile({ id }) {
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
fetchUser(id).then(
(u) => !ignore && setUser(u),
(e) => !ignore && setError(e),
);
return () => {
ignore = true;
};
}, [id]);
if (error) return <p>Error</p>;
if (!user) return <p>Loading...</p>;
return <p>{user.name}</p>;
}
useEffect | use | |
|---|---|---|
| متى يبدأ الطلب | بعد أن يكون العرض الأول على الشاشة | عندما يُنشأ الوعد، وربما قبل أن يُعرض المكوّن |
| حالة التحميل | if (!user) الخاصة بك | أقرب <Suspense> |
| الأخطاء | حالة error الخاصة بك | أقرب حدود أخطاء |
| حالات السباق | تحمي منها بنفسك (ignore) | يقرأ المكوّن دائمًا الوعد الذي أُعطي له |
| من ينشئ الوعد | المكوّن | أب، أو ذاكرة مؤقتة، أو إطار عمل |
نسخة التأثير مكتفية بذاتها، ولهذا ما زالت النهج الأشيع في التطبيقات التي بلا إطار عمل؛ وتتناولها صفحة جلب البيانات بالكامل. أما use فيتألق عندما يبدأ شيء فوق المكوّن الطلب مبكرًا ويسلّم الوعد إلى الأسفل.
الأسئلة الشائعة
ماذا يفعل الخطاف use في React؟
يُرجع use(resource) قيمة وعد أو سياق. مع الوعد يُعلَّق المكوّن حتى يُحل ويعرض أقرب <Suspense> بديله؛ وإذا رُفض الوعد، تظهر أقرب حدود أخطاء بدلًا من ذلك.
هل يمكن استدعاء use بشرط؟
نعم. use هو الخطاف الوحيد الذي يمكنك استدعاؤه داخل عبارات if والحلقات وبعد return مبكر. لكن ما زال يجب استدعاؤه من مكوّن أو خطاف، لا من معالج حدث أو تأثير أبدًا.
لماذا يدور use(fetch()) داخل مكوّني في حلقة لا نهائية؟
كل عرض ينشئ وعدًا جديدًا. يُعلَّق المكوّن عليه، وتعيد React العرض عندما يُحل، فينشئ ذلك العرض وعدًا معلقًا آخر. أنشئ الوعد خارج العرض: في حالة الأب، أو على مستوى الوحدة، أو في ذاكرة مؤقتة، أو في محمّل إطار عمل.
هل يمكنني تغليف use في try/catch؟
لا. عندما يكون الوعد معلقًا، يقاطع use العرض برمي قيمة، والتقاطها يكسره. تعامل مع الوعد المرفوض بحدود أخطاء، أو أرفق .catch() بالوعد ليُحل إلى قيمة احتياطية.
هل أستبدل جلب البيانات بـ useEffect بالخطاف use؟
يحتاج use إلى وعد يُنشأ مرة واحدة ويُعاد استخدامه، وهو ما تعطيك إياه أطر العمل ومكتبات البيانات. أما في تطبيق عميل عادي، فما زال الجلب في تأثير (أو بمكتبة مثل TanStack Query) شائعًا ولا بأس به.