💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡
قمنا بتحليل التعليقات على هذا المنشور لتقديم بعض الإرشادات: https://github.com/facebook/react/issues/14920#issuecomment -471070149.
💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡
هذه قاعدة ESLint جديدة تتحقق من قائمة تبعيات الخطافات مثل useEffect
وما شابهها ، تحمي من مخاطر الإغلاق التي لا معنى لها. بالنسبة لمعظم الحالات ، يحتوي على إصلاح تلقائي. سنضيف المزيد من الوثائق خلال الأسابيع القادمة.
yarn add eslint-plugin-react-hooks<strong i="18">@next</strong>
# or
npm install eslint-plugin-react-hooks<strong i="19">@next</strong>
تكوين ESLint:
{
"plugins": ["react-hooks"],
// ...
"rules": {
"react-hooks/rules-of-hooks": 'error',
"react-hooks/exhaustive-deps": 'warn' // <--- THIS IS THE NEW RULE
}
}
حالة اختبار بسيطة للتحقق من عمل القاعدة:
function Foo(props) {
useEffect(() => {
console.log(props.name);
}, []); // <-- should error and offer autofix to [props.name]
}
إذا تم تفعيل هذه القاعدة الجديدة react-hooks/exhaustive-deps
lint لكنك تعتقد أن شفرتك صحيحة ، يرجى النشر في هذه المشكلة.
يرجى تضمين هذه الأشياء الثلاثة:
قد يكون الأمر بسيطًا بالنسبة لك - ولكنه ليس بسيطًا على الإطلاق بالنسبة لنا. إذا كان تعليقك لا يتضمن أيًا منهما (على سبيل المثال ، لا يوجد رابط CodeSandbox) ، فسنخفي تعليقك لأنه من الصعب جدًا تتبع المناقشة بخلاف ذلك. شكرًا لك على احترام وقت الجميع من خلال تضمينهم.
الهدف النهائي من هذا الموضوع هو العثور على سيناريوهات مشتركة وتحويلها إلى مستندات وتحذيرات أفضل. يمكن أن يحدث هذا فقط عندما تتوفر تفاصيل كافية. تؤدي التعليقات التي تتضمن مقتطفات غير مكتملة من التعليمات البرمجية إلى خفض جودة المناقشة بشكل كبير - لدرجة أنها لا تستحق ذلك.
هذا المثال له رد: https://github.com/facebook/react/issues/14920#issuecomment -466145690
هذا مكون مربع اختيار غير متحكم فيه يأخذ خاصية defaultIndeterminate
لتعيين الحالة غير المحددة عند العرض الأولي (والذي يمكن إجراؤه فقط في JS باستخدام المرجع لأنه لا توجد سمة عنصر indeterminate
). الغرض من هذه الخاصية أن تتصرف مثل defaultValue
، حيث تُستخدم قيمتها فقط في التصيير الأولي.
تشكو القاعدة من أن defaultIndeterminate
مفقود من مصفوفة التبعية ، لكن إضافته ستؤدي إلى قيام المكون بالكتابة بشكل غير صحيح فوق الحالة غير المضبوطة إذا تم تمرير قيمة جديدة. لا يمكن إزالة مصفوفة التبعية تمامًا لأنها قد تتسبب في التحكم الكامل في الحالة غير المحددة بواسطة الخاصية.
لا أرى أي طريقة للتمييز بين هذا ونوع الحالة التي يجب أن تلتقطها القاعدة ، ولكن سيكون من الرائع أن تتضمن وثائق القاعدة النهائية حلاً بديلاً مقترحًا. 🙂
رد: https://github.com/facebook/react/issues/14920#issuecomment -466144466
billyjanitsch هل https://codesandbox.io/s/jpx1pmy7ry
أضفت useState
لـ indeterminate
الذي تمت تهيئته إلى defaultIndeterminate
. ثم يقبل التأثير [indeterminate]
كوسيطة. أنت لا تغيره حاليًا - ولكن إذا قمت بذلك لاحقًا ، أعتقد أن ذلك سيعمل أيضًا؟ لذا فإن الكود يتوقع حالة الاستخدام المستقبلية المحتملة بشكل أفضل قليلاً.
لقد حصلت على هذه الحالة (الحافة؟) التالية حيث قمت بتمرير بعض html واستخدمها مع dangerouslySetInnerHtml
لتحديث المكون الخاص بي (بعض المحتوى التحريري).
أنا لا أستخدم الخاصية html
لكن المرجع حيث يمكنني استخدام ref.current.querySelectorAll
للقيام ببعض السحر.
الآن أحتاج إلى إضافة html
إلى تبعياتي في useEffect
على الرغم من أنني لا أستخدمها بشكل صريح. هل هذه حالة استخدام حيث يجب علي فعلاً تعطيل القاعدة؟
الفكرة هي اعتراض جميع نقرات الارتباط من المحتوى التحريري وتتبع بعض التحليلات أو القيام بأشياء أخرى ذات صلة.
هذا مثال مخفف من مكون حقيقي:
https://codesandbox.io/s/8njp0pm8v2
أنا أستخدم رد الفعل-الإعادة ، لذا عند تمرير منشئ إجراء في الدعائم من mapDispatchToProps
، واستخدام منشئ الإجراء هذا في خطاف ، أحصل على تحذير exhaustive-deps
.
لذلك يمكنني بوضوح إضافة إجراء الإعادة إلى مصفوفة التبعية ، ولكن نظرًا لأن إجراء الإعادة هو وظيفة ولا يتغير أبدًا ، فهذا أمر غير ضروري ، أليس كذلك؟
const onSubmit = React.useCallback(
() => {
props.onSubmit(emails);
},
[emails, props]
);
أتوقع أن يقوم الوبر بإصلاح الوديعة إلى [emails, props.onSubmit]
، لكن في الوقت الحالي ، يتم دائمًا إصلاح الأقسام إلى [emails, props]
.
- رمز CodeSandbox يوضح مثالًا برمجيًا صغيرًا لا يزال يعبر عن نيتك (ليس "شريط foo" ولكن نمط واجهة المستخدم الفعلي الذي تقوم بتنفيذه).
https://codesandbox.io/s/xpr69pllmz
- شرح للخطوات التي يقوم بها المستخدم وما تتوقع رؤيته على الشاشة.
سيضيف المستخدم بريدًا إلكترونيًا ويدعو هؤلاء البريد الإلكتروني إلى التطبيق. أزلت عن قصد بقية واجهة المستخدم إلى button
لأنها ليست ذات صلة بمشكلتي.
- شرح لواجهة برمجة التطبيقات (API) المقصودة للخطاف / المكون الخاص بك.
يحتوي المكون الخاص بي على خاصية واحدة ، onSubmit: (emails: string[]) => void
. سيتم استدعاؤه بالحالة emails
عندما يرسل المستخدم النموذج.
تحرير: تمت الإجابة عليه في https://github.com/facebook/react/issues/14920#issuecomment -467494468
هذا لأن
props.foo()
الناحية الفنية يمررprops
نفسه كـthis
إلىfoo
مكالمة. لذلك قد يعتمدfoo
ضمنيًا علىprops
. سنحتاج إلى رسالة أفضل لهذه الحالة بالرغم من ذلك. أفضل الممارسات هي التدمير دائمًا.
لا يعتبر أن التحميل والتحديث يمكن أن يكونا مختلفين بشكل واضح عند التكامل عند libs الطرف الثالث. لا يمكن تضمين تأثير التحديث في mount واحد (وإزالة المصفوفة تمامًا) ، لأنه لا ينبغي إتلاف المثيل في كل عملية تصيير
أهلا،
لست متأكدًا من الخطأ في الكود الخاص بي هنا:
const [client, setClient] = useState(0);
useEffect(() => {
getClient().then(client => setClient(client));
}, ['client']);
حصلت على React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked
joelmosssylvainbaronnet أقدر ملاحظاتك ولكني أخفيت تعليقاتك لأنها لم تتضمن المعلومات التي طلبناها في الجزء العلوي من المشكلة. هذا يجعل هذه المناقشة صعبة على الجميع بلا داع لأن هناك سياق مفقود. سأكون سعيدًا لمواصلة المحادثة إذا نشرت مرة أخرى وقمت بتضمين جميع المعلومات ذات الصلة (انظر المنشور العلوي). شكرا لتفهمك.
أعتقد أن الكود كما هو لا يجب أن يخطئ. إنها تعمل الآن في السطر 35 ، قائلة إنه يجب تضمين setLastName
في المصفوفة. أي أفكار حول ما يجب القيام به حيال ذلك؟ هل أفعل شيئًا غير متوقع؟
أنا أفهم تمامًا ، وعادة ما أفعل كل ذلك من أجلك ، ولكن في حالتي المحددة ، لا يعتبر أي من الكود فريدًا بالنسبة لي. إنه مجرد سؤال حول ما إذا كان استخدام وظيفة تم تعريفها خارج الخطاف (أي منشئ إجراء إعادة الإرسال) والمستخدمة داخل الخطاف يجب أن تضاف إلى هذه الوظيفة باعتبارها نقطة ربط.
يسعدنا إنشاء رموز وصندوق إذا كنت لا تزال بحاجة إلى مزيد من المعلومات. شكرا
joelmoss أعتقد أن
نعم ، لا يزال CodeSandbox مفيدًا. يرجى تخيل ما يشبه تبديل السياق بين مقتطفات التعليمات البرمجية للأشخاص طوال اليوم. إنها خسارة ذهنية هائلة. لا أحد منهم يبدو متشابهًا. عندما تحتاج إلى تذكر كيفية استخدام الأشخاص لمنشئي الإجراءات في Redux أو بعض المفاهيم الأخرى الخارجية لـ React ، يكون الأمر أكثر صعوبة. قد تبدو المشكلة واضحة لك ولكن ليس واضحًا بالنسبة لي ما قصدته.
gaearon أفهم أنه من المنطقي ، لقد رغبتي في تحقيقه كان:
إذا كنت تريد تشغيل تأثير وتنظيفه مرة واحدة فقط (على mount and unmount) ، يمكنك تمرير مصفوفة فارغة ([]) كوسيطة ثانية.
شكرا جزيلا للتوضيح. (وآسف على الموضوع الخارج)
ها هي نسخة الكود لما أقوم بتطبيقه. لا يبدو كثيرًا ، لكن النمط مطابق بنسبة 100٪ مع الكود الحقيقي الخاص بي.
https://codesandbox.io/s/2x4q9rzwmp؟fontsize=14
كل شيء يعمل بشكل رائع في هذا المثال! باستثناء قضية لينتر.
لدي العديد من الملفات مثل هذا ، حيث أقوم بتنفيذ وظيفة في التأثير ، لكنني أريد تشغيلها فقط عندما يتم استيفاء بعض الشروط - على سبيل المثال ، تغيير معرف السلسلة. لا أريد تضمين الوظيفة في المصفوفة.
هذا ما أحصل عليه من linter عند تشغيله على كود sandbox:
25:5 warning React Hook useEffect has a missing dependency: 'fetchPodcastsFn'. Either include it or remove the dependency array react-hooks/exhaustive-deps
سؤالي هو: هل هذه هي الطريقة التي يجب أن يتصرف بها (إما قاعدة linter ، أو قاعدة صفيف الخطاف)؟ هل هناك طريقة اصطلاحية أكثر لوصف هذا التأثير؟
svenanders ، لدي فضول لمعرفة سبب عدم رغبتك في تضمين fetchPodcastsFn
؟ هل هذا لأنك تجده يتغير في كل تصيير؟ إذا كان الأمر كذلك ، فربما تريد حفظ هذه الوظيفة أو جعلها ثابتة (في حالة عدم وجود أي معلمات)
لأحد - يتعلق الأمر بالوضوح. عندما أنظر إلى الوظيفة ، أريد أن أفهم بسهولة متى يجب إطلاقها. إذا رأيت _one_ id في المصفوفة ، فسيكون ذلك واضحًا تمامًا. إذا رأيت هذا المعرف ومجموعة من الوظائف ، فإنه يصبح أكثر تشويشًا. يجب أن أقضي الوقت والجهد ، وربما حتى وظائف التصحيح ، لفهم ما يحدث.
لا تتغير وظائفي في وقت التشغيل ، لذلك لا أعرف ما إذا كان حفظها مهمًا (هذه الوظيفة على وجه الخصوص هي إجراء إرسال يطلق ملحمة ، مما يؤدي في النهاية إلى تغيير الحالة).
https://codesandbox.io/s/4xym4yn9kx
يصل المستخدم إلى مسار على الصفحة ، لكنه ليس مستخدمًا متميزًا ، لذلك نريد إعادة توجيهه بعيدًا عن الصفحة. يتم حقن props.navigate
عبر مكتبة جهاز التوجيه ، لذلك لا نريد في الواقع استخدام window.location.assign
لمنع إعادة تحميل الصفحة.
كل شيء يعمل!
لقد وضعت التبعيات بشكل صحيح كما هو الحال في صندوق حماية الكود ، لكن الوتر يخبرني أن قائمة التبعيات يجب أن تحتوي على props
بدلاً من props.navigate
. لماذا هذا؟
تغريدة مع لقطات! https://twitter.com/ferdaber/status/1098966716187582464
تحرير: أحد الأسباب الصحيحة التي قد تكون عربات التي تجرها الدواب هو إذا كان navigate()
طريقة تعتمد على this
، في هذه الحالة تقنيًا إذا تغير أي شيء داخل props
ثم الأشياء بداخلها this
سيتغير أيضًا.
CodeSandbox: https://codesandbox.io/s/711r1zmq50
API المقصود:
يسمح لك هذا الخطاف بالتخلي عن أي قيمة سريعة التغير. لن تعكس القيمة التي تم إلغاؤها آخر قيمة إلا عندما لا يتم استدعاء خطاف useDebounce خلال الفترة الزمنية المحددة. عند استخدامه جنبًا إلى جنب مع useEffect ، كما نفعل في الوصفة ، يمكنك بسهولة ضمان عدم تنفيذ العمليات باهظة الثمن مثل مكالمات API بشكل متكرر.
خطوات:
يتيح لك المثال البحث في Marvel Comic API ويستخدم useDebounce لمنع إطلاق مكالمات API عند كل ضغطة مفتاح.
على سبيل المثال ، ضع في اعتبارك استخدام الخطاف useDebounce . في استخدامات العالم الحقيقي (على الأقل في استخداماتنا) ، لن يتغير delay
بعد أول تصيير ، لكننا نتحقق مما إذا كان قد تم تغييره أم لا في كل إعادة تصيير. لذلك ، في هذا الخطاف ، من الأفضل أن تكون الوسيطة الثانية لـ useEffect
هي [value]
بدلاً من [value, delay]
.
من فضلك لا تعتقد أن عمليات التحقق من المساواة الضحلة رخيصة للغاية. يمكن أن تساعد تطبيقك عند وضعه بشكل استراتيجي ولكن مجرد جعل كل مكون نقيًا يمكن أن يجعل تطبيقك في الواقع أبطأ. المفاضلات.
أعتقد أن إضافة كل شيء في مصفوفة التبعيات ، له نفس مشكلة الأداء (أو حتى أسوأ) مثل استخدام المكونات النقية في كل مكان. نظرًا لوجود العديد من السيناريوهات التي نعلم فيها أن بعض القيم التي نستخدمها لن تتغير ، لذلك لا ينبغي أن نضيفها في مصفوفة التبعيات ، لأنه كما قال gaearon ، عمليات التحقق من المساواة الضحلة ليست رخيصة جدًا وهذا يمكن جعل تطبيقنا أبطأ.
لدي بعض التعليقات حول تمكين هذه القاعدة للعمل تلقائيًا على الخطافات المخصصة حسب الاتفاقية.
أستطيع أن أرى في الكود المصدري أن هناك نية ما للسماح للأشخاص بتحديد regex لالتقاط الخطافات المخصصة بالاسم:
ما الذي قد يفكر فيه فريق React بشأن اصطلاح تسمية إضافي للخطافات المخصصة ذات مصفوفات التبعية؟ تتبع الخطافات بالفعل اصطلاح البادئة بـ use
لكي يتم اكتشافها كخطاف بواسطة هذا المكون الإضافي. الاصطلاح الذي أقترحه لاكتشاف الخطافات المخصصة التي تعتمد على تبعيات معينة ستكون نوعًا من postfix ، شيء مثل WithDeps
، مما يعني أن اسم الخطاف المخصص الكامل يمكن أن يكون شيئًا مثل useCustomHookWithDeps
. يخبر WithDeps
postfix المكوّن الإضافي أن وسيطة المصفوفة النهائية هي إحدى التبعيات.
لا يزال بإمكان المكون الإضافي دعم regexes بشكل إضافي ، لكنني أعتقد أنني سأرى فائدة كبيرة من خلال السماح لمؤلفي المكتبات بتصدير خطافاتهم المخصصة بعد إصلاحها بـ WithDeps
بدلاً من إجبار مستهلكي المكتبة على تكوين المكون الإضافي بشكل صريح لأي وكل مخصص خطاف طرف ثالث أو غير ذلك.
إنه تحذير وإزالة تلقائية لفحص المساواة المخصصة.
const myEqualityCheck = a => a.includes('@');
useCallback(
() => {
// ...
},
[myEqualityCheck(a)]
);
بالنسبة لـ useEffect ، لا أعتقد أن تحذير "التبعية غير الضرورية" يجب أن يظهر لأن هذه "التبعيات" ستغير كيفية ظهور التأثير.
لنفترض أن لدي منضدين ، أحد الوالدين والطفل:
تطبيق:
const [parent, setParent] = useState(0);
const [child, setChild] = useState(0);
useEffect(
() => {
setChild(0);
},
[parent] // "unnecessary dependency: 'parent'"
);
تعطي القاعدة الشاملة التحذير React Hook useEffect has an unnecessary dependency: 'parent'. Either exclude it or remove the dependency array.
لا أعتقد أن linter يجب أن يقدم هذا الاقتراح لأنه يغير طريقة عمل التطبيق.
parent
التغييرات، child
يعيد إلى الصفر.parent
يغير سلوك useEffect
ولا ينبغي أن ينصحني بإزالته.[تعديل]
كحل بديل ، يمكنني كتابة شيء مثل
const [parent, setParent] = useState(0)
const [child, setChild] = useState(0)
const previousParent = useRef(parent)
useEffect(() => {
if (parent !== previousParent.current) {
setChild(0)
}
previousParent.current = parent
}, [parent])
ولكن هناك "شعر" في المقتطف الأصلي الذي أحبه.
أعتقد أن السؤال يتعلق بما إذا كان يجب علينا تفسير مصفوفة التبعية على أنها مجرد آلية لتحسين الأداء مقابل سلوك المكون الفعلي. تستخدم الأسئلة الشائعة حول React Hooks الأداء كمثال عن سبب استخدامه ، لكنني لم أفسر هذا المثال ليعني أنه يجب عليك فقط استخدام مصفوفة التبعية لتحسين الأداء ، وبدلاً من ذلك رأيت أنها طريقة ملائمة لتخطي استدعاءات التأثير بشكل عام.
إنه في الواقع نمط استخدمته عدة مرات لإبطال ذاكرة التخزين المؤقت الداخلية:
useEffect(() => {
if (props.invalidateCache) {
cache.clear()
}
}, [props.invalidateCache])
إذا كان يجب ألا أستخدمه بهذه الطريقة ، فلا تتردد في إعلامي فقط وتجاهل هذا التعليق.
تضمين التغريدة
ماذا عن
const [parent, setParent] = useState(0);
const [child, setChild] = useState(0);
const updateChild = React.useCallback(() => setChild(0), [parent]);
useEffect(
() => {
updateChild();
},
[updateChild]
);
لن أفهم ذلك المقتطف أيضًا. لا يمكن افتراض التبعية إلا بناءً على التعليمات البرمجية الخاصة بك ولكنها قد تكون خطأ. في حين أن اقتراحي لا يحل بالضرورة أنه يجعله على الأقل أكثر وضوحًا.
يبدو أنه تم حل هذا مسبقًا عبر getDerivedStateFromProps
؟ هل كيف أقوم بتطبيق getDerivedStateFromProps؟ يساعد؟
nghiepit لقد أخفيت تعليقك لأنك تجاهلت قائمة التحقق المطلوبة في المنشور الأول (مثل CodeSandbox). يرجى اتباع قائمة التحقق والنشر مرة أخرى. شكرا.
@ eps1lon سيكون لديك نفس التحذير بالضبط على useCallback
، وأنا لا أتفق مع هذا النمط بشكل عام باعتباره تحسينًا على الأصل ، لكنني لا أريد إخراج الموضوع عن مساره للتحدث عنه. للتوضيح ، أعتقد أنه يجب تخفيف قاعدة التبعية غير الضرورية لـ useEffect
أو useLayoutEffect
وجه التحديد ، لأنها يمكن أن تحتوي على منطق فعال ، ولكن يجب أن تظل القاعدة في مكانها لـ useCallback
، useMemo
، إلخ.
أواجه بعضًا من نفس المشكلات / أسئلة النموذج العقلي التي وصفها MrLeebo هنا. شعوري الداخلي هو أن قاعدة التبعية useEffect
لا يمكن أن تكون صارمة. لدي مثال مفتعل بشكل لا يصدق كنت أعمل عليه لإثبات أساسي لفكرة المفهوم. أعلم أن هذا الرمز سيء والفكرة ليست مفيدة بشكل خاص ، لكنني أعتقد أنها توضيح جيد للمشكلة المطروحة. أعتقد أن MrLeebo عبر عن السؤال الذي
أعتقد أن السؤال يتعلق بما إذا كان يجب علينا تفسير مصفوفة التبعية على أنها مجرد آلية لتحسين الأداء مقابل سلوك المكون الفعلي. تستخدم الأسئلة الشائعة حول React Hooks الأداء كمثال عن سبب استخدامه ، لكنني لم أفسر هذا المثال ليعني أنه يجب عليك فقط استخدام مصفوفة التبعية لتحسين الأداء ، وبدلاً من ذلك رأيت أنها طريقة ملائمة لتخطي استدعاءات التأثير بشكل عام.
https://codesandbox.io/s/5v9w81j244
كنت أنظر على وجه التحديد إلى الخطاف useEffect
في useDelayedItems
. سيؤدي تضمين خاصية items
في مصفوفة التبعية الآن إلى حدوث خطأ فحص ، ولكن إزالة هذه الخاصية أو المصفوفة يمنحك سلوكًا لا تريده تمامًا.
يتم إعطاء الفكرة الأساسية للخطاف useDelayedItems
مصفوفة من العناصر وكائن التكوين (تأخير بالمللي ثانية ، حجم الصفحة الأولي) ، سيتم تسليم مجموعة فرعية من العناصر بناءً على حجم الصفحة الذي تم تكوينه. بعد مرور config.delay
، ستكون العناصر الآن هي المجموعة الكاملة للعناصر. عند تسليم مجموعة جديدة من العناصر ، يجب أن يعيد الخطاف تشغيل منطق "التأخير" هذا. الفكرة عبارة عن نسخة فجّة وغبية جدًا من التأخير في تأجيل القوائم الكبيرة.
شكرًا لكل العمل على هذه القواعد ، لقد كانت مفيدة جدًا عند التطوير. حتى إذا كان المثال الخاص بي ذا جودة مشكوك فيها ، آمل أن يقدم بعض الأفكار حول كيفية استخدام هذه العوامل.
تحرير : لقد أضفت بعض التعليقات التوضيحية داخل الكود وصندوق حول نية السلوك. آمل أن تكون المعلومات كافية ، لكن يرجى إعلامي إذا كان هناك أي شيء لا يزال محيرًا.
ماذا عن قيمة واحدة تجمع بين قيم أخرى؟
مثال: الاسم الكامل مشتق من firstName
و lastName
. نريد تفعيل التأثير فقط عندما يتغير fullName
(مثل عندما ينقر المستخدم على "حفظ") ولكننا نريد أيضًا الوصول إلى القيم التي يؤلفها في التأثير
ستؤدي إضافة firstName
أو lastName
إلى التبعيات إلى كسر الأشياء لأننا نريد فقط تشغيل التأثير بعد تغيير fullName
.
aweary لست متأكدًا من القيمة التي تحصل عليها من تغيير مسار التغيير الخاص useEffect
. يبدو أن onClick
الخاص بك يجب أن يتعامل مع هذا "التأثير".
https://codesandbox.io/s/0m4p3klpyw
بقدر القيم الفردية التي تجمع بين القيم الأخرى ، من المحتمل أن يكون useMemo
هو ما تريده. الطبيعة المتأخرة للعملية الحسابية في مثالك تعني أنها لا تحدد بالضبط 1: 1 بسلوكك المرتبط.
سوف أقوم بإنشاء روابط رموز وعلب لهذه الأمثلة وتعديل هذا المنشور.
لدي قاعدة بسيطة للغاية: إذا تغيرت علامة التبويب الحالية ، فانتقل إلى الأعلى:
https://codesandbox.io/s/m4nzvryrxj
useEffect(() => {
window.scrollTo(0, 0);
}, [activeTab]);
وحصلت على React Hook useEffect has an unnecessary dependency: 'activeTab'. Either exclude it or remove the dependency array
أيضًا ، عندما كنت أقوم بترحيل بعض المكونات ، أريد تكرار سلوك componentDidMount:
useEffect(() => {
...
}, []);
هذه القاعدة تشكو بشدة من هذا. أنا متردد في إضافة كل تبعية إلى مجموعة useEffect.
أخيرًا ، إذا قمت بتعريف دالة مضمنة جديدة قبل useEffect ، مثل هذا:
https://codesandbox.io/s/nr7wz8qp7l
const foo = () => {...};
useEffect(() => {
foo();
}, []);
تحصل على: React Hook useEffect has a missing dependency: 'foo'. Either include it or remove the dependency array
لست متأكدًا من شعوري حيال إضافة foo إلى قائمة التبعيات. في كل تصيير ، هل وظيفة جديدة وسيبدأ useEffect دائمًا في العمل؟
@ ksaldana1 وضع التأثير الجانبي داخل معالج الحدث ليس كافيًا. قد يتسبب ذلك في حدوث التأثير الجانبي قبل الالتزام بالتحديث الفعلي ، مما قد يتسبب في تشغيل التأثير الجانبي أكثر مما تريد.
كما أنه لن يعمل عند استخدام useReducer
لأن الحالة المحدثة لن تكون متاحة داخل معالج الحدث.
بقدر القيم الفردية التي تجمع بين القيم الأخرى ، من المحتمل أن يكون
useMemo
هو ما تريده.
إذا استخدم هذا المثال useMemo
فسوف ينكسر لأن useMemo
سيشتق fullName
جديدًا في كل مرة يتغير فيها firstName
أو lastName
. السلوك هنا هو أن fullName
لا يتم تحديثه حتى يتم النقر فوق الزر Save.
aweary هل شيء مثل هذا العمل؟ https://codesandbox.io/s/lxjm02j50m
أنا فضولي للغاية لمعرفة ما هو التنفيذ الموصى به.
أشعر بالفضول أيضًا لسماع المزيد عن شيئين ذكرتهما. هل يمكنك أن تدلني على المزيد من المعلومات حول هذه؟
التشغيل والتأثير في المعالج:
قد يتسبب ذلك في حدوث التأثير الجانبي قبل الالتزام بالتحديث الفعلي ، مما قد يتسبب في تشغيل التأثير الجانبي أكثر مما تريد.
باستخدام useReducer
state في معالج الحدث:
الحالة المحدثة لن تكون متاحة.
شكرا!
bugzpodder نوع غير مرتبط ولكن يجب أن يكون استدعاء التمرير داخل useLayoutEffect
بدلاً من useEffect
. يوجد حاليًا وميض ملحوظ عند الانتقال إلى المسار الجديد
لست متأكدًا مما إذا كان هذا مقصودًا أم لا:
عندما تستدعي دالة من الدعائم ، يقترح linter إضافة كائن الدعائم بأكمله كعنصر تبعية.
نسخة مختصرة:
useEffect(function() {
props.setLoading(true)
}, [props.setLoading])
// ⚠️ React Hook useEffect has a missing dependency: 'props'.
النسخة الكاملة: Codesandbox
المحاولة مرة أخرى ... ولكن أبسط هذه المرة ؛)
عند تمرير دالة في الدعائم ، أو في الواقع من أي مكان ، واستخدام هذه الوظيفة داخل خطاف ، أحصل على تحذير exhaustive-deps
.
لذا يمكنني بوضوح إضافة الدالة إلى مصفوفة التبعية ، ولكن لأنها دالة ولا تتغير أبدًا ، فهذا أمر غير ضروري ، أليس كذلك؟
-> Sandbox
آمل أن يكون هذا هو كل ما تحتاجه ، لكنني ببساطة قمت بتقسيم رمل
شكرا.
لذا يمكنني بوضوح إضافة الدالة إلى مصفوفة التبعية ، ولكن لأنها دالة ولا تتغير أبدًا ، فهذا أمر غير ضروري ، أليس كذلك؟
هذا ليس صحيحا؛ يمكن أن تتغير الوظائف طوال الوقت عندما يعيد الوالد تصييرها.
تضمين التغريدة
عندما تستدعي دالة من الدعائم ، يقترح linter إضافة كائن الدعائم بأكمله كعنصر تبعية.
هذا بسبب قيام props.foo()
الناحية الفنية بتمرير props
نفسه كـ this
إلى foo
مكالمة. لذلك قد يعتمد foo
ضمنيًا على props
. سنحتاج إلى رسالة أفضل لهذه الحالة بالرغم من ذلك. أفضل الممارسات هي التدمير دائمًا.
يمكن أن تتغير الوظائف طوال الوقت عندما يعيد الوالد تصييرها.
بالتأكيد ، ولكن إذا تم تحديد الوظيفة في مكان آخر وليس من مكون رئيسي ، فلن تتغير أبدًا.
بالتأكيد ، ولكن إذا تم تحديد الوظيفة في مكان آخر وليس من مكون رئيسي ، فلن تتغير أبدًا.
إذا قمت باستيراده مباشرة ، فلن تطلب منك قاعدة النسالة إضافته إلى أقسام التأثير. فقط إذا كان في نطاق العرض. إذا كان موجودًا في نطاق التجسيد ، فإن قاعدة الوبر لا تعرف من أين أتت. حتى لو لم يكن الأمر ديناميكيًا اليوم ، فقد يكون غدًا عندما يغير شخص ما المكون الرئيسي. لذا فإن تحديده هو الخيار الصحيح. لا يضر تحديده إذا كان ثابتًا على أي حال.
تضمين التغريدة
هذا بسبب قيام
props.foo()
الناحية الفنية بتمريرprops
نفسه كـthis
إلىfoo
مكالمة. لذلك قد يعتمدfoo
ضمنيًا علىprops
. سنحتاج إلى رسالة أفضل لهذه الحالة بالرغم من ذلك. أفضل الممارسات هي التدمير دائمًا.
هذا يجيب على سؤالي أيضًا. شكرا لك! 😄
https://codesandbox.io/s/98z62jkyro
لذلك أقوم بإنشاء مكتبة للتعامل مع التحقق من صحة الإدخال عن طريق تسجيل جميع المدخلات باستخدام واجهة برمجة تطبيقات مكشوفة في سياق لكل إدخال لتسجيل أنفسهم. لقد قمت بإنشاء خطاف مخصص يسمى useRegister.
السابق:
useEffect(
() => {
register(props.name, props.rules || []);
console.log("register");
return function cleanup() {
console.log("cleanup");
unregister(props.name);
};
},
[props.name, props.rules, register, unregister]
);
عند إجبار props.rules على أن تكون جزءًا من التبعيات ، يبدو أنها تنتهي في حلقة تصيير لانهائية. Props.rules عبارة عن مجموعة من الوظائف التي يجب تسجيلها كمدققين. لقد اكتشفت هذه المشكلة فقط عند تقديم المصفوفات كتبعيات. في صندوق الرموز الخاص بي ، يمكنك أن ترى أنه يفتح وحدة التحكم.
مجرد وجود اسم props.name على أنه تبعية يجعله يعمل على النحو المنشود. سيؤدي فرض التبعيات إلى تغيير سلوك التطبيق كما هو مشار إليه ، وفي هذه المناسبة تكون الآثار الجانبية شديدة.
تضمين التغريدة
رد: https://github.com/facebook/react/issues/14920#issuecomment -467212561
useEffect(() => {
window.scrollTo(0, 0);
}, [activeTab]);
هذا يبدو وكأنه قضية شرعية. سأقوم بتخفيف التحذير للسماح بدخول التأثيرات الخارجية فقط. (ولكن ليس من أجل useMemo
أو useCallback
.)
أيضًا ، عندما كنت أقوم بترحيل بعض المكونات ، أريد تكرار سلوك componentDidMount:
هذه القاعدة تشكو بشدة من هذا. أنا متردد في إضافة كل تبعية إلى مجموعة useEffect.
عذرًا ، لم تضف أي مثال على ذلك ، لذا فقدنا الفرصة لمناقشة ذلك. هذا هو بالضبط سبب طلب منشور OP تحديد مثال محدد لواجهة المستخدم . لقد فعلت ذلك بالنسبة للنقطة الأولى ولكن ليس هذه النقطة. يسعدني مناقشته عند إضافة مثال ملموس له. التفاصيل تعتمد حقًا على ذلك.
أخيرًا ، إذا قمت بتعريف دالة مضمنة جديدة قبل useEffect ، مثل هذا: https://codesandbox.io/s/nr7wz8qp7l
في هذه الحالة ، يكون الحل السهل هو نقل doSomething
إلى التأثير. إذن فأنت لست بحاجة إلى التصريح بذلك. بدلاً من ذلك ، يمكنك useCallback
بحوالي doSomething
. أنا منفتح على تخفيف القاعدة للسماح بحذف الوظائف التي تستخدم الإدارات المعلنة فقط. لكن هذا يمكن أن يكون مربكًا إذا كانت لديك وظيفة تستدعي وظيفة أخرى ، وتضيف خاصية أو حالة إلى إحدى تلك الوظائف. فجأة يجب تحديث كل التأثيرات التي تستخدمها بشكل عابر. يمكن أن تصبح مربكة.
لا أعرف ما إذا كان هذا طلب ميزة لقاعدة جديدة ، أو شيء يمكن تحسينه حول exhaustive-deps
…
import React from 'react'
import emitter from './thing'
export const Foo = () => {
const onChange = () => {
console.log('Thing changed')
}
React.useEffect(() => {
emitter.on('change', onChange)
return () => emitter.off('change', onChange)
}, [onChange])
return <div>Whatever</div>
}
نظرًا لأن الدالة onChange
يتم إنشاؤها في كل عرض ، فإن الوسيطة useEffect
hook [onChange]
زائدة عن الحاجة ، وقد تتم إزالتها أيضًا:
React.useEffect(() => {
emitter.on('change', onChange)
return () => emitter.off('change', onChange)
- }, [onChange])
+ })
يستطيع linter اكتشاف ذلك ، وينصحك بحذف وسيطة المصفوفة.
كانت هناك مواقف احتفظت فيها بقائمة من عناصر المصفوفة ، فقط لأدرك أنه تم إنشاء واحد أو أكثر منها وإلغاء ربط كل عرض على أي حال.
لقد أصدرت للتو [email protected]
مع بعض الإصلاحات ورسائل أفضل لهذه القاعدة. لا ينبغي حل أي شيء غير مسبوق سوى عدد قليل من الحالات. سأبحث في الباقي الأسبوع المقبل.
لقد قمت أيضًا بنشر الخطوة الأولى الممكنة لحذف أقسام الوظائف "الآمنة" هنا: https://github.com/facebook/react/pull/14996. (انظر الاختبارات.) إذا كانت لديك أفكار حول الاستدلال المفيد وكيفية توجيه الناس إلى الإصلاحات الصحيحة ، يرجى التعليق على العلاقات العامة.
gaearon فكرة ممتازة. سيكون هذا مفيدًا بالتأكيد للحصول على أسلوب أفضل عند استخدام الخطافات 🙏
gaearon لا يزال لا يعمل في حالة أن الإجراء يأتي من الدعامة. ضع في اعتبارك هذا المثال:
حيث يكون setScrollTop
إجراء إعادة.
في هذا المثال في المكون Slider
، أستخدم useEffect
للانتظار حتى يتوفر DOM حتى يمكنني تحميل مكون noUiSlider. لذلك قمت بتمرير [sliderElement]
للتأكد من أن المرجع متاح في DOM عند تشغيل التأثير. نقوم أيضًا بعرض مكوناتنا على الخادم ، لذلك يضمن هذا أيضًا توفر DOM قبل العرض. الدعائم الأخرى التي أستخدمها في useEffect
(أي min ، max ، onUpdate ، إلخ) هي ثوابت ، وبالتالي لا أرى حاجة لتمريرها إلى التأثير.
هذا هو التأثير كما هو موضح في codeandbox:
const { min, max, step } = props;
const sliderElement = useRef();
useEffect(() => {
if (!sliderElement.current) {
return;
}
const slider = sliderElement.current;
noUiSlider.create(slider, {
connect: true,
start: [min, max],
step,
range: {
min: [min],
max: [max],
},
});
if (props.onUpdate) {
slider.noUiSlider.on('update', props.onUpdate);
}
if (props.onChange) {
slider.noUiSlider.on('change', props.onChange);
}
}, [sliderElement]);
@ WebDeg-Brian لا يمكنني مساعدتك بدون عرض CodeSandbox كامل. آسف. انظر أعلى وظيفة.
لقد نشرت القليل عن المفاهيم الخاطئة الشائعة "الوظائف لا تتغير أبدًا":
https://overreacted.io/how-are-function-components-different-from-classes/
ليس نفس الموضوع تمامًا ، ولكنه ذو صلة بهذه القاعدة.
مرحبًا gaearon ، هذا هو المثال الذي طلبت مني نشره هنا (من مكبر الصوت) :)
في الأساس أحاول تحويل مصيدة رد فعل مكتبتي إلى خطافات.
هذا مجرد فخ للأحداث ، خارج / داخل عنصر.
مشكلتي هي أنه إذا كان useEffect
لا يعتمد على قيمة الحالة ( trapped
) ، فإنه أحيانًا يكون قديمًا.
لقد كتبت بعض التعليقات والسجلات للتوضيح. انظر إلى ملف useTrap.js
، التعليقات والسجلات موجودة في الدالتين useEffect
و preventDefaultHelper
.
على حد علمي ، إذا لم تكن القيمة داخل useEffect
فلا ينبغي أن تكون جزءًا من تبعياتها (صححني إذا كنت مخطئًا).
e.preventDefault
).آمل أن أكون واضحًا هنا وأن حالة الاستخدام مفهومة ، يرجى إعلامي إذا كنت بحاجة إلى تقديم مزيد من المعلومات. شكرا!
مرحبًا gaearon ، أقوم بإضافة اقترحتني في Twitter! أجد صعوبة في العثور على الشكل الصحيح الذي لا يتخطى التبعيات.
إنه مفصل كمثال ، آمل أن أتمكن من شرحه بطريقة واضحة ومفهومة.
هذه هي الحالة الحالية لها: رد فعل غير متزامن-utils / src / hooks / useAsyncData.ts
نظرة عامة على الوظيفة
مساعدة المستخدم في التعامل مع المكالمات غير المتزامنة والبيانات الناتجة وحالتها طوال العملية.
triggerAsyncData
التحديثات asyncData
state بشكل غير متزامن وفقًا للدالة getData
التي تُرجع Promise
. يمكن استدعاء triggerAsyncData
كتأثير أو "يدويًا" بواسطة مستخدم الخطاف.
التحديات
triggerAsyncData
هي تلك التبعيات الضمنية. triggerAsyncData
هو تبعية للتأثير ، ولكن يتم إنشاؤه في كل تصيير. خط الأفكار حتى الآن:useMemo
/ useCallback
مع triggerAsyncData
=> يجب استخدام useMemo
/ useCallback
فقط لتحسين الأداء بقدر ما أعلم.triggerAsyncData
كتبعية ، استخدم تبعيات triggerAsyncData
كاعتماديات => أفضل خيار وجدته حتى الآن. لكنها تخرق قاعدة "الاستنفاد".useMemo
/ useCallback
إذا لزم الأمر => أخشى أنهم لن يفعلوا ذلك في كثير من الأحيان. وإذا فعلوا ذلك فهو مطوّل تمامًا.asyncData
). هذا يكسر قاعدة "استنفاد" مرة أخرى.شرح أطول مما كنت أتمنى ... لكنني أعتقد أنه يعكس كفاحي لاستخدام الخطافات بطريقة مناسبة. يرجى إعلامي إذا كان هناك شيء آخر يمكنني القيام به لتوضيح هذه الصعوبات.
شكرا لك على كل العمل الشاق هنا!
يا gaearon شكرا على كل ما تبذلونه من العمل الشاق.
الحد الأدنى من البيانات غير المتزامنة في جلب مثال على مثال CodeSandbox .
من المتوقع أن يرى المستخدم 5 سلاسل عنوان lorem ipsum تم جلبها من
لقد أنشأت خطافًا مخصصًا لجلب البيانات باستخدام واجهة برمجة التطبيقات (API) المقصودة:
const { data, isLoading, isError } = useDataApi('https://jsonplaceholder.typicode.com/posts')
الأجزاء الداخلية للخطاف المخصص useDataApi
:
...
const fetchData = async () => {
let response;
setIsError(false);
setIsLoading(true);
try {
response = await fetch(url).then(response => response.json());
setData(response);
} catch (error) {
setIsError(true);
}
setIsLoading(false);
};
useEffect(
() => {
fetchData();
},
[url]
);
...
المشكلة هي هذا الرمز
useEffect(
() => {
fetchData();
},
[url]
);
حيث يقوم react-hooks/exhaustive-deps
بإصدار تحذير بأنه يجب علي إضافة fetchData
إلى مصفوفة التبعية الخاصة بي ويجب إزالة url
.
إذا غيرت هذا الخطاف إلى
useEffect(
() => {
fetchData();
},
[fetchData]
);
ثم يتم إطلاق الطلبات باستمرار ولا يتوقف أبدًا ، وهو بالطبع مشكلة كبيرة. لست متأكدًا مما إذا كان الكود الخاص بي يحتوي على أخطاء برمجية أم أن react-hooks/exhaustive-deps
يطلق نتيجة إيجابية خاطئة.
نقدر أي مساعدة. شكرا جزيلا.
ملاحظة: لقد قرأت تعليقك حول useEffect غير مناسب لجلب البيانات ، ومع ذلك ، تدعي مستندات التفاعل أن Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects
مما منحني الثقة في أن البيانات useEffect
رائعة لجلب البيانات. لذلك أنا الآن مرتبك قليلاً 😕
@ jan-stehlik يجب أن تغلف fetchData مع useCallback. قمت بتقسيم الأكواد الخاصة بك مع التغيير الضروري هنا https://codesandbox.io/s/pjmjxprp0m
مفيد جدا ، شكرا جزيلا viankakrisna !
على حد علمي ، إذا لم تكن القيمة داخل useEffect ، فلا ينبغي أن تكون جزءًا من تبعياتها (صححني إذا كنت مخطئًا).
أعتقد أنك مخطئ هنا. أو بالأحرى ، مرتبك بعض الشيء. كنت تستخدم handleEvent
داخل تأثير الخاص بك. لكنك لا تعلن ذلك. هذا هو السبب في أن القيم التي تقرأها قديمة.
مثير للإعجاب.
أنت تستخدم handleEvent داخل تأثيرك. لكنك لا تعلن ذلك. هذا هو السبب في أن القيم التي تقرأها قديمة.
ماذا تقصد ب: _ "لكنك لا تصرح بذلك" _؟
تم التصريح عنه تحت التأثير (مثل كل المعالجات الأخرى).
أم تقصد لأن المعالج يستخدم قيمة الحالة ، والتأثير هو إرفاق المعالج ، فهذا يعني أن التأثير يعتمد على قيمة هذه الحالة؟
أم تقصد لأن المعالج يستخدم قيمة الحالة ، والتأثير هو إرفاق المعالج ، فهذا يعني أن التأثير يعتمد على قيمة هذه الحالة؟
نعم ، إذا كنت تستخدم دالة ، فيجب عليك إما التصريح عنها في الأقسام (وفي هذه الحالة لفها بـ useCallback
لتجنب إعادة إنشائها) ، أو كل شيء تستخدمه هذه الوظيفة.
حسنًا ، هذه أخبار بالنسبة لي! شكرا لك على هذه المدخلات gaearon :)
أريد فقط أن يكون الأمر واضحًا جدًا بالنسبة لي (وللآخرين؟) ...
إذا كان التأثير يستدعي أو يمرر أو يفعل أي شيء بوظيفة ، فنحن بحاجة إلى تمرير إلى مصفوفة deps إما:
الدالة نفسها أو المتغيرات التي تستخدمها هذه الدالة.
إذا تم الإعلان عن الوظيفة داخل مكون الوظيفة / الخطاف المخصص ، يُنصح بلفها بـ useCallback
حتى لا يتم إعادة إنشائها في كل مرة يتم فيها تشغيل المكون أو الخطاف المخصص.
يجب أن أقول أنني لم أره في
هل تعتقد أنه من الجيد إضافته إلى قسم _Note_؟
ملحوظة
لا يتم تمرير مصفوفة المدخلات كوسيطات لوظيفة التأثير. من الناحية المفاهيمية ، هذا ما يمثلونه: كل قيمة مشار إليها داخل دالة التأثير يجب أن تظهر أيضًا في مصفوفة المدخلات. في المستقبل ، يمكن لمترجم متقدم بشكل كافٍ إنشاء هذه المصفوفة تلقائيًا.
يحرر
شيء آخر ، في المثال الخاص بي ، ما هي الودائع لـ useCallback
عندما تلتف حول handleEvent
(أو أي معالجات أخرى لهذا السبب). هل هي event
هي نفسها؟
يجب أن أقول أنني لم أره في المستندات.
تقول المستندات "كل قيمة مشار إليها داخل دالة التأثير يجب أن تظهر أيضًا في مصفوفة المدخلات". الوظائف هي قيم أيضًا. أوافق على أننا بحاجة إلى توثيق هذا بشكل أفضل - وهو ما يدور حوله هذا الموضوع :-) أنا أجمع حالات الاستخدام لصفحة توثيق جديدة مخصصة لهذا الموضوع.
هناك شيء آخر ، في المثال الذي قدمته ، ما هي الأجزاء المخصصة لـ useCallback عندما يلتف handleEvent (أو أي معالجات أخرى لهذا السبب). هل هذا هو الحدث الذاتي؟
لست متأكدا مما تقصده. إنها أي قيم تشير إليها الوظيفة خارجها. تمامًا مثل useEffect
.
أعتقد أنني لم أفكر في هذا من خلال وظائف مثل التبعيات. كان نموذجي العقلي خاطئًا ، كنت أفكر في تمرير وظيفة كاعتماد فقط إذا تم تقديمها كدعم أو حجة للمكون / الخطاف الخاص بي. شكرا لتوضيح هذا بالنسبة لي.
بالنسبة إلى useCallback
، فقد استخدمته على النحو التالي:
const memoHandleEvent = useCallback(
handleEvent
);
وبالطبع مررت memoHandleEvent
كتبعية لـ useEffect
أيضًا إلى addEventListener
داخل الوظيفة الحقيقية handleEvent
. يبدو أنه يعمل ، أتمنى أن تكون هذه هي الطريقة الصحيحة والاصطلاحية للقيام بذلك.
لاحظ أن useCallback
بدون الوسيطة الثانية لا يفعل شيئًا.
مرة أخرى ، سيكون من الضروري وجود رمل كامل. لا يمكنني معرفة ما إذا كان الإصلاح الخاص بك صحيحًا من خلال وصف غير مكتمل مثل هذا.
لاحظ أن useCallback بدون وسيطة ثانية لا تفعل شيئًا.
أرج! : الكشر: لول
مرة أخرى ، سيكون من الضروري وجود رمل كامل. لا يمكنني معرفة ما إذا كان الإصلاح الخاص بك صحيحًا من خلال وصف غير مكتمل مثل هذا.
عفوًا ، هذا هو نفس الرابط أعلاه. لقد قمت بتحديثه للتو :)
الرجاء عدم تحديث روابط CodeSandbox: يحتاج PI إليها كما كانت في الأصل - وإلا لا يمكنني اختبار قاعدة الوبر عليها. هل يمكنك إنشاء صندوقين رمل منفصلين إذا كان لديك حلان مختلفان؟ لذلك يمكنني التحقق من كل واحد.
عفوا آسف! : PI تجاوزت عدد صناديق الحماية في حسابي. دعني أحذف بعضها وسأنشئ واحدًا آخر (وأعيد التغييرات في الأصل).
gaearon هذا هو الرابط الثاني للحل بـ useCallback
لدي سيناريو أعتقد أنه بخير ، لكن اللنتر يشكو. عينتي:
ما تحاول العينة تمثيله هو عندما ينقر المستخدم على زر ، ثم يتم تقديم طلب لطلب بيانات جديدة بناءً على معرف ووظيفة تم توفيرها مسبقًا. إذا تغير المعرّف ، فلا يجب أن يطلب بيانات جديدة ؛ يجب أن يؤدي طلب إعادة التحميل الجديد فقط إلى تشغيل طلب بيانات جديد.
المثال هنا مفتعل قليلاً. في تطبيقي الحقيقي ، تعيش React في عنصر DIV يمثل جزءًا صغيرًا من تطبيق ويب أكبر بكثير. الوظيفة التي يتم تمريرها هي عبر Redux و mapDispatchToProps ، حيث يأخذ إنشاء الإجراء المعرف ويقدم طلب ajax لجلب البيانات وتحديث المتجر. يتم تمرير الخاصية RefreshRequest عبر React.createElement. في تطبيقي الأصلي ، كان لدي رمز يشبه هذا في مكون الفصل الدراسي:
componentDidUpdate (prevProps) {
const { getData, someId, refreshRequest} = this.props;
if (prevProps.refreshRequest!== this.props.refreshRequest) {
getData(someId);
}
}
أحاول تنفيذ نفس السلوك مع تأثير الخطاف. لكن كما هو مكتوب في العينة ، يشتكي Linter:
تحذير لدى React Hook useEffect تبعيات مفقودة: 'getData' و 'someId'. قم إما بتضمينها أو إزالة مصفوفة التبعية
إذا أضفت كل ما يريده linter ، فعندئذٍ إذا نقر المستخدم على أي من الزرين في العينة ، يتم تشغيل useEffect. لكنني أريد فقط أن يتم تشغيله عند الضغط على زر طلب بيانات جديدة.
نأمل أن يكون ذلك منطقيًا. سأكون سعيدًا للتوضيح أكثر ، إذا كان هناك شيء غير واضح. شكرا لك!
لقد قمت للتو بنشر [email protected]
والذي يحتوي على دعم تجريبي لاكتشاف تبعيات الوظائف المجردة (والتي لا تميل إلى أن تكون مفيدة جدًا بدون useCallback
). هنا صورة gif:
أتمنى أن تجربها في مشاريعك وترى كيف تشعر! (يرجى التعليق على هذا التدفق المحدد في https://github.com/facebook/react/pull/15026.)
سأحاول على أمثلة من هذا الموضوع غدا.
لم أجربها بعد ولكني أتساءل عما إذا كانت تتعامل مع الرفع. هذا مجرد "مثال أدنى" توصلت إليه في الحال ، لذا فهو ليس مفيدًا لأي شيء ، لكنني أستخدم الإعلانات المرفوعة كثيرًا لتسهيل رؤية بيان return
.
function Component() {
useEffect(() => {
handleChange
}, [handleChange])
return null
function handleChange() {}
}
سيكون من الجيد أن تحتوي القاعدة على خيار لتكوين وظيفة أخرى لتتصرف مثل useEffect
بقدر ما يتعلق الأمر بـ linter. على سبيل المثال، وأضفت هذا حتى أتمكن من استخدامها بسهولة المتزامن وظائف للآثار التي تفعل مكالمة AJAX - ولكن، مثل هذا أفقد كل exhaustive-deps
فوائد linting:
const useAsyncEffect = (fn, ...args) => {
/* eslint-disable react-hooks/exhaustive-deps */
useEffect(() => {
fn();
}, ...args);
};
تحرير: لا بأس ، لقد لاحظت للتو أن هذا ممكن بالفعل باستخدام الخيار additionalHooks
للقاعدة.
تحية للجميع،
لدي مثال https://codesandbox.io/s/znnmwxol7l
فيما يلي ما أريده:
function App() {
const [currentTime, setCurrentTime] = React.useState(moment());
const currentMonth = React.useMemo(
() => {
console.log("RUN");
return currentTime.format("MMMM");
},
[currentTime.format("MMMM")] // <= this proplem [currentTime]
);
return (
<div className="App">
<h1>Current month: {currentMonth}</h1>
<div>
<button
onClick={() => setCurrentTime(currentTime.clone().add(1, "days"))}
>
+ 1 day
</button>
</div>
<div>{currentTime.toString()}</div>
</div>
);
}
لكن هذا ما أحصل عليه:
const currentMonth = React.useMemo(
() => {
console.log("RUN");
return currentTime.format("MMMM");
},
[currentTime] // <= this proplem
);
في أي وقت تغيير currentTime
ثم إعادة حساب currentMonth
غير ضروري
أو بطريقة ما يمكنني القيام بذلك أدناه:
const currentMonth = React.useMemo(
() => {
return currentTime.format("MMMM");
},
[myEqualityCheck(currentTime)]
);
آسف إذا تم الرد على هذا بالفعل ، لا يمكن رؤيته في الموضوع أعلاه:
طريقة تشغيل الخطاف useEffect فقط على الحامل هو تحديد مصفوفة مدخلات فارغة كمعامل ثانٍ. ومع ذلك ، عند القيام بذلك ، فإن الشكاوى الشاملة حول تضمين حجج الإدخال ، والتي من شأنها تغيير التأثير ليتم تشغيله أيضًا عند التحديث.
ما هو الأسلوب المتبع لتشغيل useEffect فقط على الحامل مع تمكين التراكيب الشاملة؟
einarq أعتقد أنك بحاجة إلى التأكد من أن جميع القيم المشار إليها في حسابك useEffect
لن تتغير أبدًا. يمكن تحقيق ذلك باستخدام خطافات أخرى مثل useMemo
. بعد ذلك ، بغض النظر عما إذا كانت قاعدة ESlint هذه تضيف جميع المراجع إلى المصفوفة (مع الإصلاح التلقائي) أم لا ، سيتم تنفيذ الكود مرة واحدة فقط.
einarq كما هو مذكور في مكان آخر في الموضوع ، لا يمكننا مساعدتك بدون CodeSandbox. لأن الإجابة تعتمد حقًا على الكود الخاص بك.
nghiepit مثالك ليس له معنى حقًا بالنسبة لي. إذا كان الإدخال الخاص بك هو currentTime.format("MMMM")
فلن يقوم useMemo
بتحسين أي شيء من أجلك لأنك قمت بحسابه بالفعل . لذا فأنت تقوم بحسابها مرتين دون داع.
هل من الممكن تحديد فهرس الوسيطة الذي يقوم بإعادة الاتصال على خيار additionalHooks
؟ أرى أننا نفترض الآن في الكود أنه سيكون أول https://github.com/facebook/react/blob/9b7e1d1389e080d19e71680bbbe979ec58fa7389/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L1051 - L1081
غير ممكن حاليا. إرشاداتنا العامة هي أن الخطافات المخصصة يجب أن تفضل عدم وجود وسيطة deps
لأنه يصبح من الصعب جدًا التفكير في كيفية إنشاء الوصلات. إذا كنت تأخذ وظيفة ، فقد تريد أن تطلب من المستخدمين useCallback
بدلاً من ذلك.
CarlosGines هل يمكنك إنشاء CodeSandbox ، على النحو المطلوب في منشور OP. أطلب هذا لسبب ما - وإلا فإنه يصعب علي التحقق من التغييرات. خاصة عندما يتم كتابتها في TypeScript.
لقد نشرت للتو [email protected]
مع رسائل مفيدة أكثر واستدلالًا أكثر ذكاءً. يرجى تجربتها في مشاريعك قبل أن تصبح مستقرة.
أتوقع أنه مع معظم الأمثلة في هذا الموضوع ، يجب أن يقدم نصيحة معقولة من شأنها أن تساعدك في حل المشكلات.
يجب أن يكون هذا [email protected]
؟
نعم فعلا.
في الواقع تم نشر [email protected]
مع بعض التغييرات الصغيرة.
gaearon هل تمكنت من الحصول على eslint
للعمل داخل codeandbox بأي فرصة؟
einarq ربما شيء من هذا القبيل هل سيعمل هذا؟
const useDidMount = fn => {
const callbackFn = useCallback(fn)
useEffect(() => {
callbackFn()
}, [callbackFn])
}
أشعر بالحرج حقًا لأقول أنه لا يمكنني تشغيل المكون الإضافي على تطبيق CRA بسيط (استنادًا إلى مقتطفتي ).
هنا هو الريبو المتشعب من codeandbox.
لقد رأيت هذه الملاحظة في المستندات :
ملاحظة: إذا كنت تستخدم تطبيق Create React ، فالرجاء الانتظار للإصدار المقابل لنصوص التفاعل التي تتضمن هذه القاعدة بدلاً من إضافتها مباشرةً.
ولكن لا توجد طريقة لاختباره مع CRA في الوقت الحالي؟ 👂
نعم ، يجب عليك إخراجها وإضافتها إلى تكوين ESLint حتى نضيفها هناك. يمكنك إخراجها في فرع وليس دمجها. :-)
أهلا،
بادئ ذي بدء: شكرًا جزيلاً على العمل عن كثب مع المجتمع والعمل الرائع الذي تقوم به بشكل عام!
لدينا مشكلة في الخطاف المخصص الذي أنشأناه حول Fetch
API. لقد قمت بإنشاء Codesandbox لتوضيح المشكلة.
https://codesandbox.io/s/kn0km7mzv
ملاحظة: ينتج عن المشكلة (انظر أدناه) حلقة لا نهائية ولا أريد DDoS jsonplaceholder.typicode.com
الذي أستخدمه لتوضيح المشكلة. لذلك ، قمت بتضمين محدد طلب بسيط باستخدام عداد. هذا ليس مطلوبًا لإثبات المشكلة ولكن شعرت أنه من الخطأ إرسال عدد غير محدود من الطلبات إلى هذا المشروع العظيم.
تكمن الفكرة في تسهيل التعامل مع الحالات الثلاث المحتملة لطلب واجهة برمجة التطبيقات: التحميل والنجاح والخطأ. وبالتالي قمنا بإنشاء خطاف مخصص useFetch()
والذي يعرض 3 خصائص isLoading
، response
و error. It makes sure that either
استجابة or
خطأ is set and updates
isLoading . As the name implies, it uses the
Fetch` API.
للقيام بذلك ، يستخدم خطافات 3 useState
:
const [isLoading, setIsLoading] = useState(false);
const [response, setResponse] = useState(null);
const [error, setError] = useState(null);
وخطاف useEffect
:
useEffect(
() => {
fetch(url, fetchConfig)
.then(/* Handle fetch response... See Codesandbox for the actual implementation */)
},
[url]
);
إنه يعمل بشكل جيد طالما أن مصفوفة التبعيات useEffect
تحتوي فقط على [url]
. إذا أضفنا fetchConfig
(= [url, fetchConfig]
) ، سينتج عن ذلك حلقة لا نهائية. بالنسبة لحالة الاستخدام الخاصة بنا ، سيكون كافيًا إعادة تشغيل التأثير فقط عندما يتغير url
ولكن لا يسمح الوعاء باستخدام [url]
(تم اختباره بـ v1.4.0
و v1.5.0-beta.1
).
في نهاية الخطاف المخصص ، يتم إرجاع متغيرات الحالة الثلاثة ككائن:
return {
error,
isLoading,
response
};
لست متأكدًا مما إذا كان هذا هو المكان المناسب لطلب التوجيه بشأن هذه المسألة لأن اقتراح الفحص منطقي ، على ما أعتقد. آسف إذا لم يكن كذلك.
ماذا لو تغير fetchConfig
؟ لماذا تتوقع أن تكون ثابتة؟
حسنا. أصدرت [email protected]
مع إصلاحات وتحسينات الأسبوع الماضي.
كان هذا الخيط مفيدًا للغاية في العثور على أنماط مختلفة تسبب المشاكل.
شكرا جزيلا لكم جميعا. (خاصة أولئك الذين قدموا صناديق الحماية. :-)
لن أرد على الجميع شخصيًا بالتفصيل لأنه يوجد الكثير من الحالات.
بدلاً من ذلك ، سنقوم بكتابة الوصفات والأخطاء الشائعة في الأيام القليلة القادمة ، وسنقوم بربطها من المستندات. سأتأكد من تغطية كل نمط شائع في هذا الموضوع (أو أطلب المتابعة في قضية منفصلة للأنماط النادرة).
بمجرد نشر الأمثلة ، سأعلق هنا وأعدل كل تعليق يحتوي على CodeSandbox من خلال إلحاق رابط بالإجابة / المثال ذي الصلة الذي يوضح الإصلاح الصحيح. آمل أن يساعدك هذا والقراء في المستقبل.
هتافات!
❤️
timkraut يجب أن تكون قادرًا على إضافة fetchConfig
على الأقسام ، ويجب أن
مثال: https://codesandbox.io/s/9l015v2x4w
مشكلتي مع هذا هي أن المكون يحتاج الآن إلى أن يكون على دراية بتفاصيل تنفيذ الخطاف ...
لست متأكدًا مما إذا كان هذا الموضوع لا يزال مفتوحًا للمناقشة على سبيل المثال ، لكنني ما زلت ألتف حول أفضل الممارسات. سؤالي يدور حول التحكم في وقت اشتعال الخطاف useEffect مع مصفوفة التبعية واستخدام القيم في ذلك الوقت مقابل طلب إعلان مرجع منفصل للالتفاف على قاعدة النسالة.
https://codesandbox.io/s/40v54jnkyw
في هذا المثال ، أحاول حفظ قيم الإدخال تلقائيًا بشكل دوري.
كل 5 ثوانٍ ، يحاول الكود حفظ القيم الحالية تلقائيًا (ما عليك سوى طباعتها على الشاشة في الوقت الحالي). سيتطلب linter أن يتم تضمين جميع قيم الإدخال في مصفوفة التبعية والتي ستغير عدد مرات إطلاق التأثير.
useEffect(
() => {
if (autoSaveTick % 5 === 0) {
setAutosaveValue(
`${input1Value.value}, ${input2Value.value}, ${input3Value.value}`
);
}
},
[autoSaveTick]
);
البديل الذي أراه هو وجود تطبيق مشابه للخطافات المحددة useTick
و useInterval
في المثال ، حيث يوجد رد اتصال مخصص لمرجع. يبدو أن هذا قد يتسبب في إعادة تعريف الوظائف غير الضرورية فقط من أجل التوافق مع قاعدة النسالة.
أنا أبحث عن أفضل الممارسات في كتابة تأثير يتم تشغيله عندما يتغير متغير واحد (متغير أ) يستخدم قيمًا متغيرة أخرى (متغير ب ومتغير ج) في وقت تغيير المتغير السابق (المتغير أ).
ماذا لو تغير
fetchConfig
؟ لماذا تتوقع أن تكون ثابتة؟
سأكون بخير لإضافته إلى مصفوفة التبعيات. في حالة العمل الخاصة بنا ، لا يمكن أن يحدث هذا أبدًا ولكن أعتقد أنه من الجيد إضافته على أي حال.
مشكلتي مع هذا هي أن المكون يحتاج الآن إلى أن يكون على دراية بتفاصيل تنفيذ الخطاف ...
هذه بالضبط هي المشكلة التي نواجهها أيضًا: / في تطبيقنا ، استخدمنا خاصية إضافية لتسهيل ضبط الجسم ، لذا سيتعين علينا استخدام مكالمات 2 useMemo()
كلما استخدمنا هذا الخطاف. لست متأكدًا من كيفية التغلب على هذا القيد ، حتى الآن. إذا كان لديك فكرة ، أعلمني!
كيف يمكنك تشغيل useEffect الذي له تبعيات مرة واحدة فقط دون أن تشكو القاعدة عند تمرير مصفوفة فارغة؟
قل شيئًا مثل:
useEffect(() => {
init({ dsn, environment})
}, [])
مشكلتي مع هذا هي أن المكون يحتاج الآن إلى أن يكون على دراية بتفاصيل تنفيذ الخطاف ...
لن أقول إن هذه تفاصيل التنفيذ. إنها واجهة برمجة التطبيقات الخاصة بك. إذا تم تمرير كائن ما ، فإننا نفترض أنه يمكن أن يتغير في أي وقت.
إذا كانت ثابتة دائمًا من الناحية العملية ، فيمكنك جعلها حجة لمصنع هوك. مثل createFetch(config)
الذي يُرجع useFetch()
. اتصل بالمصنع على أعلى مستوى.
أنا أفهم أنه غريب بعض الشيء. لدينا مشكلة مماثلة مع useSubscription
الذي نعمل عليه. ولكن نظرًا لأنها مشكلة شائعة ، فقد يعني هذا أن useMemo
هو في الواقع إجابة شرعية ويجب أن يعتاد الناس على القيام بذلك في هذه الحالات.
من الناحية العملية - سننظر أكثر في هذا في المستقبل. لكن لا يجب أن تعامل كائنًا ديناميكيًا محتملًا ككائن ثابت لأنه لا يمكن للمستخدم بعد ذلك تغييره.
asylejmani لم تقدم أي تفاصيل حول حالات الاستخدام الخاصة بك ، كما هو مطلوب في
الهدف من القاعدة هو إخبارك أن environment
و dsn
يمكن أن يتغير بمرور الوقت ويجب أن يتعامل المكون الخاص بك مع ذلك. لذلك إما أن المكون الخاص بك هو عربات التي تجرها الدواب (لأنه لا يتعامل مع التغييرات على هذه القيم) أو لديك بعض الحالات الفريدة حيث لا يهم (في هذه الحالة يجب عليك إضافة قاعدة lint وتجاهل التعليق الذي يوضح السبب).
في كلتا الحالتين ليس من الواضح ما الذي تطلبه. تشكو القاعدة من أن الأشياء يمكن أن تتغير ، وأنت لا تتعامل مع هذا التغيير. بدون مثال كامل ، يمكنك وحدك الإجابة عن سبب اعتقادك أن التعامل معها ليس ضروريًا.
gaearon آسف لعدم الوضوح (يبدو الأمر واضحًا دائمًا في
كنت تحت الانطباع أكثر مما تفعله مصفوفة فارغة ، لكن القاعدة تخبرني أن أدرج دائمًا التبعيات في المصفوفة.
asylejmani أعتقد أن أكبر مشكلة مع طرق دورة حياة الفصل مثل componentDidMount
هي أننا نميل إلى التفكير فيها على أنها طريقة منعزلة ، لكنها في الحقيقة جزء من التدفق.
إذا أشرت إلى شيء ما في componentDidMount
فستحتاج على الأرجح إلى التعامل معه في componentDidUpdate
أيضًا ، أو قد يتعرض المكون الخاص بك إلى أخطاء.
هذا ما تحاول القاعدة إصلاحه ، فأنت بحاجة إلى التعامل مع القيم بمرور الوقت.
الرقم 1 هو المكان الذي تضع فيه المنطق في componentDidMount
/ useEffect
body
الرقم 2 هو المكان الذي تضع فيه المنطق في componentDidUpdate
/ useEffect
Deps
تشكو القاعدة من أنك لا تقوم بالجزء الثاني من التدفق
gaearon آسف لعدم الوضوح (يبدو الأمر واضحًا دائمًا في
كنت تحت الانطباع أكثر مما تفعله مصفوفة فارغة ، لكن القاعدة تخبرني أن أدرج دائمًا التبعيات في المصفوفة.
أعتقد أنني و asylejmani على نفس الصفحة هنا ، لكن أعتقد أن ما تقوله gaearon هو أننا ربما نكون مخطئين في تشغيل التأثير على mount فقط إذا كان لدينا بالفعل تبعيات.
هل هذا بيان عادل؟ أعتقد أنني أفكر في أن توفير مصفوفة فارغة يشبه نوعًا ما قول "أنا أعرف ما أفعله" ، لكنني أفهم سبب رغبتك في الاستمرار في تطبيق القاعدة.
آسف لعدم توفير رمل حتى الان. بدأت الليلة الماضية بمثال Create React App ، ولم أتمكن من معرفة كيفية تشغيل eslint على sandbox ، ثم فقدت صندوق الحماية عند إعادة تحميل المتصفح دون الحفظ أولاً (من المفترض أن يكون CodeSandbox temp مخزنة ، سيئة).
ثم اضطررت إلى النوم ، ولم يكن لدي وقت منذ ذلك الحين.
على أي حال ، أحصل على ما تقوله ، وأنه في ظل السيناريوهات العادية ، من الأفضل على الأرجح تضمين تلك التبعيات وعدم افتراض أنها كافية للتشغيل على التثبيت فقط.
من المحتمل أن تكون هناك حالات استخدام صالحة لذلك أيضًا ، ولكن من الصعب بعض الشيء شرحها أو الخروج بمثال جيد ، لذلك سأعيش مع تعطيل القاعدة المضمنة عند الحاجة.
asylejmani هل حالة استخدامك مشابهة لـ https://github.com/facebook/react/issues/14920#issuecomment -466378650؟ لا أعتقد أنه من الممكن للقاعدة أن تفهم السيناريو في هذه الحالة ، لذلك سنحتاج فقط إلى تعطيله يدويًا لهذا النوع من التعليمات البرمجية. في جميع الحالات الأخرى ، تعمل القاعدة كما ينبغي.
لست متأكدًا مما إذا كان هذا منطقيًا ، ولكن أحد السيناريوهات الشائعة جدًا بالنسبة لي هو شيء من هذا القبيل:
useEffect(() => {
if(!dataIsLoaded) { // flag from redux
loadMyData(); // redux action creator
}
}, []);
كل من هذه التبعيات تأتي من الإعادة. يجب تحميل البيانات (في هذه الحالة) مرة واحدة فقط ، ومنشئ الإجراء هو نفسه دائمًا.
هذا خاص بالإعادة ، ولا يمكن لقاعدة eslint الخاصة بك معرفة ذلك ، لذا فهمت لماذا يجب أن تحذر. ما زلت أتساءل عما إذا كان تقديم مصفوفة فارغة ربما يؤدي فقط إلى تعطيل القاعدة؟ يعجبني أن القاعدة تخبرني عن فقدان الأقسام إذا قدمت بعضها وليس كلها ، أو إذا لم أقدم أيًا منها على الإطلاق. المصفوفة الفارغة تعني شيئًا مختلفًا بالنسبة لي. لكن هذا قد يكون أنا فقط :)
شكرا لكل عملك الشاق! ولجعل حياتنا كمطورين أفضل :)
حالة الاستخدام الخاصة بي أبسط بكثير ويمكنني بالطبع إضافة جميع التبعيات وستظل تعمل بنفس الطريقة ، ولكن كان لدي انطباع بأن القاعدة "ستحذر" عندما يكون لديك بعض التبعيات وتفتقد أخرى.
حالة استخدام einarq هي شيء استخدمته عدة مرات ، على سبيل المثال عند تحميل "componentDidMount" البيانات إذا لم يكن هناك أي شيء (من redux أو أي شيء آخر).
أوافق أيضًا على أنه في هذه الحالات ، يكون تعطيل القاعدة المضمنة هو الخيار الأفضل. هذه الحالة تعرف بالضبط ما تفعله.
أعتقد أن ارتباكي كان [] ضد [بعض] ، وبالطبع شكرًا
أعتقد أنني و asylejmani على نفس الصفحة هنا ، لكن أعتقد أن ما تقوله gaearon هو أننا ربما نكون مخطئين في تشغيل التأثير على mount فقط إذا كان لدينا بالفعل تبعيات. هل هذا بيان عادل؟
نعم فعلا. إذا كان المكون الخاص بك لا يتعامل مع تحديثات الدعامة ، فعادة ما يكون هناك أخطاء في عربات التي تجرها الدواب. تصميم useEffect
يجبرك على مواجهته. يمكنك بالطبع حل هذه المشكلة ، ولكن الافتراضي هو حثك على التعامل مع هذه الحالات. يشرح هذا التعليق جيدًا: https://github.com/facebook/react/issues/14920#issuecomment -470913287.
كل من هذه التبعيات تأتي من الإعادة. يجب تحميل البيانات (في هذه الحالة) مرة واحدة فقط ، ومنشئ الإجراء هو نفسه دائمًا.
إذا كان الأمر هو نفسه ، فلن يؤذيك تضمينه في التبعيات. أريد التأكيد على ذلك - إذا كنت متأكدًا من أن تبعياتك لا تتغير أبدًا ، فلا ضرر من إدراجها . ومع ذلك ، إذا حدث لاحقًا أنها تغيرت (على سبيل المثال ، إذا مر مكون رئيسي بوظيفة مختلفة اعتمادًا على الحالة) ، فسيتعامل المكون الخاص بك مع ذلك بشكل صحيح.
ما زلت أتساءل عما إذا كان تقديم مصفوفة فارغة ربما يؤدي فقط إلى تعطيل القاعدة؟
لا ، إن توفير مصفوفة فارغة ثم التساؤل عن سبب تقادم بعض العناصر أو الحالة هو الخطأ الأكثر شيوعًا.
>
له معنى كبير ، شكرا
في 8 آذار (مارس) 2019 ، الساعة 15:27 ، كتب Dan Abramov [email protected] :
أعتقد أنني و asylejmani على نفس الصفحة هنا ، لكن أعتقد أن ما تقوله gaearon هو أننا ربما نكون مخطئين في تشغيل التأثير على mount فقط إذا كان لدينا بالفعل تبعيات. هل هذا بيان عادل؟
نعم فعلا. إذا كان المكون الخاص بك لا يتعامل مع تحديثات الدعامة ، فعادة ما يكون هناك أخطاء في عربات التي تجرها الدواب. تصميم useEffect يجبرك على مواجهته. يمكنك بالطبع حل هذه المشكلة ، ولكن الافتراضي هو حثك على التعامل مع هذه الحالات. هذا التعليق يفسره جيدا: # 14920 (تعليق).
كل من هذه التبعيات تأتي من الإعادة. يجب تحميل البيانات (في هذه الحالة) مرة واحدة فقط ، ومنشئ الإجراء هو نفسه دائمًا.
إذا كان الأمر هو نفسه ، فلن يؤذيك تضمينه في التبعيات. أريد التأكيد على ذلك - إذا كنت متأكدًا من عدم تغير تبعياتك أبدًا ، فلا ضرر من إدراجها. ومع ذلك ، إذا حدث لاحقًا أنها تغيرت (على سبيل المثال ، إذا مر مكون رئيسي بوظيفة مختلفة اعتمادًا على الحالة) ، فسيتعامل المكون الخاص بك مع ذلك بشكل صحيح.
ما زلت أتساءل عما إذا كان تقديم مصفوفة فارغة ربما يؤدي فقط إلى تعطيل القاعدة؟
لا ، إن توفير مصفوفة فارغة ثم التساؤل عن سبب تقادم بعض العناصر أو الحالة هو الخطأ الأكثر شيوعًا.
-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذه الرسالة الإلكترونية مباشرةً ، أو اعرضها على GitHub ، أو قم بكتم صوت الموضوع.
تضمين التغريدة
قد يتسبب ذلك في حدوث التأثير الجانبي قبل الالتزام بالتحديث الفعلي ، مما قد يتسبب في تشغيل التأثير الجانبي أكثر مما تريد.
لست متأكدًا مما يعنيه هذا. يمكن أن توفر لنا مثالا؟
لقد قمنا بتمرير هذه مع threepointone اليوم. هنا ملخص:
useEffect
لا تمنعك القاعدة من إضافة إدارات "خارجية" إلى useEffect
بعد الآن نظرًا لوجود سيناريوهات شرعية.
لا يحذر Linter من الحالات التي يكون فيها الوضع آمنًا الآن ، ولكنه في جميع الحالات الأخرى يمنحك اقتراحات أفضل (مثل نقل الوظيفة داخل التأثير ، أو تغليفها بـ useCallback
).
لا ينتج عن هذا انتهاكات الوبر بعد الآن ولكن الطريقة الاصطلاحية لإعادة ضبط الحالة استجابةً للدعائم مختلفة . سيحتوي هذا الحل على عرض غير متسق إضافي لذلك لست متأكدًا من أنه مرغوب فيه.
تدفعك الخطافات نحو الصواب حيثما أمكن ذلك. إذا كنت تفعل تحديد DEPS (والتي في بعض الحالات يمكنك حذف)، ونحن نوصي بشدة لتشمل حتى تلك التي كنت تعتقد لن تتغير. نعم ، في هذا المثال useDebounce
من غير المرجح أن يتغير التأخير. لكنه لا يزال يمثل خطأ إذا حدث ذلك ، لكن الخطاف لا يمكنه التعامل معه. يظهر هذا في سيناريوهات أخرى أيضًا. (على سبيل المثال ، تعتبر الخطافات أكثر توافقًا مع إعادة التحميل الساخن لأن كل قيمة يتم التعامل معها على أنها ديناميكية.)
إذا كنت تصر تمامًا على أن قيمة معينة ثابتة ، فيمكنك فرضها.
الطريقة الأكثر أمانًا هي القيام بذلك بشكل صريح في واجهة برمجة التطبيقات الخاصة بك:
const useFetch = createFetch({ /* config object */});
const useDebounce = createDebounce(500);
const FormInput = createInput({ rules: [emailValidator, phoneValidator] });
ثم من الواضح أنه لا يمكن تغييره ما لم تضعه داخل تصيير. (والذي لن يكون استخدامًا اصطلاحيًا للخطاف الخاص بك.) لكن القول بأن <Slider min={50} />
لا يمكن تغييره أبدًا ليس صحيحًا حقًا - يمكن لأي شخص تغييره بسهولة إلى <Slider min={state ? 50 : 100} />
. في الواقع ، يمكن لشخص ما القيام بذلك:
let slider
if (isCelsius) {
slider = <Slider min={0} max={100} />
} else {
slider = <Slider min={32} max={212} />
}
إذا قام شخص ما بتبديل isCelsius
في الحالة ، فإن المكون الذي يفترض أن min
لا يتغير أبدًا سيفشل في التحديث. ليس من الواضح في هذه الحالة أن Slider
سيكون هو نفسه (لكن سيكون ذلك لأنه يحتوي على نفس الموضع في الشجرة). إذن ، هذا مسدس قدم رئيسي من حيث إجراء تغييرات على الكود. تتمثل إحدى النقاط الرئيسية في React في أن التحديثات تُعرض تمامًا مثل الحالات الأولية (لا يمكنك عادةً تحديد أيها). سواء كنت ترسم قيمة الخاصية B ، أو انتقلت من قيمة الخاصية A إلى B - يجب أن تبدو وتتصرف بنفس الطريقة.
في حين أن هذا غير مستحسن ، في بعض الحالات ، يمكن أن تكون آلية التنفيذ عبارة عن خطاف يحذر عندما تتغير القيمة (ولكنها توفر العنصر الأول). على الأقل بعد ذلك من المرجح أن يتم ملاحظته.
function useMyHook(a) {
const initialA = usePossiblyStaleValue(a);
// ...
}
function usePossiblyStaleValue(value) {
const ref = useRef(value);
if (process.env.NODE_ENV !== 'production') {
if (ref.current !== value) { // Or your custom comparison logic
console.error(
'Unlike normally in React, it is not supported ' +
'to pass dynamic values to useMyHook(). Sorry!'
);
}
}
return ref.current;
}
قد تكون هناك أيضًا حالة مشروعة حيث لا يمكنك التعامل مع التحديث. على سبيل المثال ، إذا كانت واجهة برمجة التطبيقات ذات المستوى الأدنى لا تدعمها ، مثل مكون jQuery الإضافي أو واجهة برمجة تطبيقات DOM. في هذه الحالة ، يظل التحذير مناسبًا حتى يفهمه مستهلك المكون الخاص بك. بدلاً من ذلك ، يمكنك إنشاء مكون غلاف يعيد تعيين key
على التحديثات غير المتوافقة - مما يفرض إعادة التثبيت النظيف باستخدام الدعائم الجديدة. ربما يكون هذا هو الأفضل لمكونات الأوراق مثل المنزلقات أو مربعات الاختيار.
بادئ ذي بدء ، إذا كان ثابتًا ومرفوعًا إلى نطاق المستوى الأعلى ، فلن يشتكي linter. لكن هذا لا يساعد في الأمور التي تأتي من الدعائم أو السياق.
إذا كان حقا ثابتا ثم تحديد في DEPS لا يضر. مثل الحالة التي يتم فيها إرجاع دالة setState
داخل خطاف مخصص إلى المكون الخاص بك ، ثم تقوم باستدعائها من أحد التأثيرات. قاعدة الوبر ليست ذكية بما يكفي لفهم المراوغة مثل هذا. ولكن من ناحية أخرى ، يمكن لأي شخص التفاف رد الاتصال هذا لاحقًا قبل العودة ، وربما يشير إلى خاصية أو حالة أخرى بداخلها. إذن لن يكون الأمر ثابتًا! وإذا فشلت في التعامل مع هذه التغييرات ، فستواجه أخطاء خاصية / حالة سيئة. لذا فإن تحديده يعد تقصيرًا أفضل.
ومع ذلك ، فمن المفاهيم الخاطئة أن قيم الوظيفة ثابتة بالضرورة. غالبًا ما تكون ثابتة في الفئات بسبب طريقة الربط ، على الرغم من أن ذلك يخلق نطاقًا خاصًا من الأخطاء . لكن بشكل عام ، لا يمكن اعتبار أي دالة تغلق فوق قيمة في مكون دالة ثابتة. أصبحت قاعدة الوبر الآن أكثر ذكاءً في إخبارك بما يجب عليك فعله. (مثل تحريكه داخل التأثير - أبسط إصلاح - أو تغليفه بـ useCallback
.)
هناك مشكلة في الطيف المقابل لهذا ، حيث تحصل على حلقات لا نهائية (تتغير قيمة الوظيفة دائمًا). نلاحظ ذلك في قاعدة النسالة الآن عندما يكون ذلك ممكنًا (في نفس المكون) ونقترح إصلاحًا. لكن الأمر صعب إذا تجاوزت شيئًا ما بعدة مستويات.
لا يزال بإمكانك تغليفها بـ useCallback
لإصلاح المشكلة. تذكر أنه من الناحية الفنية يمكن تغيير الوظيفة ، ولا يمكنك تجاهل هذه الحالة دون المخاطرة بالأخطاء. مثل onChange={shouldHandle ? handleChange : null}
أو عرض foo ? <Page fetch={fetchComments /> : <Page fetch={fetchArticles />
في نفس المكان. أو حتى fetchComments
الذي يغلق فوق حالة المكون الأصلي. يمكن أن يتغير. مع الفئات ، سيتغير سلوكها بصمت لكن مرجع الوظيفة سيبقى كما هو. لذلك سيفتقد طفلك هذا التحديث - ليس لديك حقًا خيار خارج تمرير المزيد من البيانات إلى الطفل. مع مكونات الوظيفة و useCallback
، تتغير هوية الوظيفة نفسها - ولكن فقط عند الضرورة. لذلك فهذه خاصية مفيدة وليست مجرد عائق يجب تجنبه.
يجب أن نضيف حلاً أفضل لاكتشاف الحلقات غير المتزامنة اللانهائية. يجب أن يخفف هذا الجانب الأكثر إرباكًا. قد نضيف بعض الكشف عن هذا في المستقبل. يمكنك أيضًا كتابة شيء مثل هذا بنفسك:
useWarnAboutTooFrequentChanges([deps]);
هذا ليس مثاليًا وسنحتاج إلى التفكير في التعامل مع هذا بأمان أكثر. أوافق على أن مثل هذه الحالات سيئة للغاية. سيكون الإصلاح دون كسر القاعدة هو جعل rules
ثابتًا ، على سبيل المثال عن طريق تغيير API إلى createTextInput(rules)
، ولف register
و unregister
إلى useCallback
. والأفضل من ذلك ، قم بإزالة register
و unregister
، واستبدلها بسياق منفصل حيث تضع dispatch
بمفردها. ثم يمكنك أن تضمن أنك لن تحصل على هوية دالة مختلفة عن قراءتها.
أود أن أضيف أنك ربما تريد وضع useMemo
على قيمة السياق على أي حال لأن الموفر يقوم بالكثير من العمليات الحسابية التي سيكون من المحزن تكرارها إذا لم يتم تسجيل مكونات جديدة ولكن تم تحديث الأصل الخاص به. لذا فإن هذا النوع من الأمور يضع المشكلة التي ربما لم تلاحظها بطريقة أخرى أكثر وضوحًا. على الرغم من أنني أوافق على أننا بحاجة إلى جعلها أكثر بروزًا عند حدوث ذلك.
يؤدي تجاهل تبعيات الوظائف تمامًا إلى أخطاء أسوأ في مكونات الوظيفة والخطافات لأنها ستستمر في رؤية الدعائم التي لا معنى لها وتذكر إذا قمت بذلك. لذا حاول ألا تفعل ذلك عندما تستطيع.
إنه أمر غريب بالنسبة لي لماذا يستخدم هذا المثال تأثيرًا لشيء يعد في الأساس معالج حدث. إن القيام بنفس "السجل" (أفترض أنه يمكن أن يكون نموذج إرسال) في معالج الحدث يبدو أكثر ملاءمة. هذا صحيح بشكل خاص إذا فكرنا في ما يحدث عند فك المكون. ماذا لو تم فكها مباشرة بعد جدولة التأثير؟ أشياء مثل إرسال النموذج لا يجب أن "تحدث" فقط في هذه الحالة. لذلك يبدو أن التأثير قد يكون خيارًا خاطئًا هناك.
ومع ذلك ، لا يزال بإمكانك فعل ما جربته - بجعل fullName
يكون setSubmittedData({firstName, lastName})
بدلاً من ذلك ، ثم [submittedData]
هو التبعية الخاصة بك ، والتي يمكنك من خلالها قراءة firstName
و lastName
.
عند الدمج مع أشياء ضرورية مثل مكونات jQuery الإضافية أو واجهات برمجة تطبيقات DOM الخام ، قد يُتوقع بعض السذاجة. ومع ذلك ، ما زلت أتوقع منك أن تكون قادرًا على دمج التأثيرات أكثر قليلاً في هذا المثال.
أتمنى ألا أنسى أحداً! اسمحوا لي أن أعرف إذا فعلت أو إذا كان هناك شيء غير واضح. سنحاول تحويل الدروس من هذا إلى بعض المستندات قريبًا.
gaearon ، شكرًا على الوقت الذي (# 14920 (تعليق) بواسطةtrevorgithub) في
أفترض أن العينة الخاصة بي ستندرج ضمن "التكامل مع التعليمات البرمجية الحتمية / القديمة" ، على الرغم من احتمال وجود فئة (فئات) أخرى أيضًا؟
في حالة مشكلات "التكامل مع التعليمات البرمجية الحتمية / القديمة" ، يبدو أنه قد لا يكون هناك الكثير مما يمكن القيام به. في هذه الحالات ، كيف يمكن للمرء أن يتجاهل هذا التحذير؟ أعتقد:
// eslint-disable-line react-hooks/exhaustive-deps
آسف فاتني هذا.
إذا تلقيت بعض البيانات عبر الخاصيات ولكنك لا تريد استخدام هذه الخاصيات حتى يتم إجراء بعض التغييرات الصريحة ، فيبدو أن الطريقة الصحيحة لنمذجة الحالة هي الحصول على الحالة المشتقة.
أنت تفكر في الأمر على أنه "أريد تجاهل التغيير في الدعامة حتى دعم آخر". ولكن يمكنك أيضًا التفكير في الأمر على أنه "يحتوي المكون الخاص بي على وظيفة جلب في الحالة. يتم تحديثه من خاصية عندما تتغير خاصية أخرى ".
لا يُنصح بالحالة المشتقة عمومًا ولكن هنا تبدو كما تريد. إن أبسط طريقة لتنفيذ ذلك ستكون مثل:
const [currentGetData, setCurrentGetData] = useState(getData);
const [prevRefreshRequest, setPrevRefreshRequest] = useState(refreshRequest);
if (prevRefreshRequest !== refreshRequest) {
setPrevRefreshRequest(refreshRequest);
setCurrentGetData(getData);
}
useEffect(() => {
currentGetData(someId);
}, [currentGetData, someId]);
ستحتاج أيضًا إلى وضع useCallback
بحوالي getData
الذي تقوم بنقله.
لاحظ أنه بشكل عام ، يبدو نمط تمرير الوظائف غير المتزامنة للجلب مظللًا بالنسبة لي. أعتقد في حالتك أنك تستخدم Redux بحيث يكون ذلك منطقيًا. ولكن إذا تم تحديد وظيفة غير متزامنة في مكون أصلي ، فسيكون ذلك مريبًا لأنه من المحتمل أن يكون لديك ظروف عرقية. لا تحتوي تأثيراتك على عملية تنظيف ، فكيف يمكنك معرفة متى يختار المستخدم معرّفًا مختلفًا؟ هناك خطر من وصول الطلبات معطلة وتعيين الحالة الخاطئة. لذلك هذا مجرد شيء يجب مراعاته. إذا كان جلب البيانات موجودًا في المكون نفسه ، فيمكنك الحصول على وظيفة تنظيف التأثير التي تحدد علامة "تجاهل" لمنع setState من الاستجابة. (بالطبع ، غالبًا ما يكون نقل البيانات إلى ذاكرة تخزين مؤقت خارجية حلاً أفضل - وهذه هي الطريقة التي سيعمل بها Suspense أيضًا.)
إن القيام بنفس "السجل" (أفترض أنه يمكن أن يكون نموذج إرسال) في معالج الحدث يبدو أكثر ملاءمة.
gaearon المشكلة التي أراها هي أن القيام بذلك في معالج الحدث يعني أن التأثير الجانبي يحدث قبل الالتزام بالتحديث. لا يوجد ضمان صارم بأن المكون سيعيد تصيير بنجاح نتيجة لهذا الحدث ، لذا فإن القيام بذلك في معالج الحدث قد يكون سابقًا لأوانه.
على سبيل المثال ، إذا كنت أرغب في تسجيل الدخول إلى أن المستخدم قد أرسل بنجاح استعلام بحث جديدًا ويقوم بعرض النتائج. إذا حدث خطأ ما وألقى المكون ، فلن أرغب في حدوث حدث السجل هذا.
ثم هناك الحالة التي قد يكون فيها هذا التأثير الجانبي غير متزامن ، لذا فإن استخدام useEffect
يمنحك وظيفة التنظيف.
هناك أيضًا مشكلة useReducer
، حيث لن تكون القيمة التي قد أرغب في تسجيلها متاحة في معالج الأحداث. لكن أعتقد أن هذا موجود بالفعل على الرادار
في كلتا الحالتين ، من المحتمل أن يكون النهج الذي توصي به كافياً. قم بتخزين الحالة المكونة في نموذج حيث لا يزال بإمكانك الوصول إلى القيم الفردية التي تكوّنها.
لدي خطاف ملائم لوظائف التغليف مع معلمات إضافية وتمريرها. تبدو هكذا:
function useBoundCallback(fn, ...bound) {
return useCallback((...args) => fn(...bound, ...args), [fn, ...bound]);
}
(إنه في الواقع أكثر تعقيدًا بعض الشيء لأنه يحتوي على بعض الميزات الإضافية ولكن ما ورد أعلاه لا يزال يُظهر المشكلة ذات الصلة).
حالة الاستخدام هي عندما يحتاج أحد المكونات إلى تمرير خاصية ما إلى طفل ، ويريد للطفل أن يكون لديه طريقة لتعديل تلك الخاصية المحددة. على سبيل المثال ، ضع في اعتبارك قائمة تحتوي على خيار تعديل لكل عنصر. يمرر الكائن الأصل قيمة إلى كل طفل ، ودالة نداء لاستدعاء ما إذا كان يرغب في تحرير القيمة. لا يعرف الطفل معرّف العنصر الذي يتم عرضه ، لذلك يجب على الوالد تعديل رد الاتصال لتضمين هذه المعلمة. يمكن أن يتداخل هذا مع العمق العشوائي.
يظهر مثال مبسط لهذا التدفق هنا: https://codesandbox.io/s/vvv36834k5 (النقر على "Go!" يظهر سجل وحدة التحكم الذي يتضمن مسار المكونات)
المشكلة هي أنني أحصل على خطأين linter مع هذه القاعدة:
يحتوي React Hook (X) على تبعية مفقودة: "ملزمة". قم إما بتضمينه أو إزالة مصفوفة التبعية
يحتوي React Hook (X) على عنصر انتشار في مصفوفة التبعية الخاصة به. هذا يعني أنه لا يمكننا التحقق بشكل ثابت مما إذا كنت قد تجاوزت التبعيات الصحيحة أم لا
سيؤدي تغييرها لاستخدام قائمة المعلمات (أي عدم استخدام عامل الانتشار) إلى تدمير الذاكرة ، لأنه يتم إنشاء القائمة مع كل استدعاء حتى لو كانت المعلمات متطابقة.
أفكار:
مرحبًا gaearon ، لقد تلقيت للتو هذا التحذير ، والذي لم
Accessing 'myRef.current' during the effect cleanup will likely read a different ref value because by this time React has already updated the ref. If this ref is managed by React, store 'myRef.current' in a variable inside the effect itself and refer to that variable from the cleanup function.
أجد هذه الرسالة محيرة بعض الشيء. أعتقد أنه يحاول تحذيري من أن قيمة المرجع الحالية عند التنظيف يمكن أن تختلف عن القيمة في جسم التأثير. حق؟
إذا كان الأمر كذلك ، وكونك على علم به ، فهل من الآمن / الشرعي تجاهل هذا التحذير؟
حالتي ، إذا كانت مثيرة للاهتمام: CodeSandbox
السياق: ربط بعض البيانات المخصصة. أستخدم عدادًا في المرجع لمنع ظروف السباق والتحديثات على المكونات غير المثبتة.
أعتقد أنه يمكنني التحايل على هذا التحذير بإخفاء قراءة المرجع داخل دالة ، أو إنشاء مرجع منطقي آخر لحالة التنظيف. لكني أجد مسهبا بشكل غير ضروري إذا كان بإمكاني تجاهل هذا التحذير.
تضمين التغريدة
على سبيل المثال ، إذا كنت أرغب في تسجيل الدخول إلى أن المستخدم قد أرسل بنجاح استعلام بحث جديدًا ويقوم بعرض النتائج. إذا حدث خطأ ما وألقى المكون ، فلن أرغب في حدوث حدث السجل هذا.
نعم ، هذا يبدو وكأنه حالة استخدام متخصصة. أعتقد أنه عندما يريد معظم الناس "الآثار الجانبية" فإنهم يقصدون تقديم النموذج نفسه - وليس حقيقة أنك شاهدت نموذجًا مقدمًا. في هذه الحالة ، يبدو الحل الذي قدمته جيدًا.
@ davidje13
هل يستحق إظهار الخطأ الأول عندما ينطبق الخطأ الثاني أيضًا؟
يرجى تقديم قضية جديدة لمقترحات تغيير قاعدة الوبر.
هل هناك أي طريقة يمكنني من خلالها تعطيل هذه القاعدة على وجه التحديد للأماكن التي يتم فيها استخدام عامل انتشار؟
يمكنك دائمًا // eslint-disable-next-line react-hooks/exhaustive-deps
إذا كنت تعتقد أنك تعرف ما تفعله.
إذا كان الاستخدام الوحيد للمتغير داخل الوظيفة هو مع عامل الانتشار ، فمن الآمن استخدام عامل الانتشار في التبعيات ، وبما أن القاعدة تكتشف ذلك بالفعل ، فمن المؤكد أنها يجب أن تسمح بذلك.
تقديم قضية جديدة من فضلك.
تضمين التغريدة
أجد هذه الرسالة محيرة بعض الشيء. أعتقد أنه يحاول تحذيري من أن قيمة المرجع الحالية عند التنظيف يمكن أن تختلف عن القيمة في جسم التأثير. حق؟
نعم فعلا.
إذا كان الأمر كذلك ، وكونك على علم به ، فهل من الآمن / الشرعي تجاهل هذا التحذير؟
أمم .. ليس إذا أدى ذلك إلى خلل. 🙂
السياق: ربط بعض البيانات المخصصة. أستخدم عدادًا في المرجع لمنع ظروف السباق والتحديثات على المكونات غير المثبتة.
نعم ، ربما تكون حالة الاستخدام هذه شرعية. تقديم قضية جديدة لمناقشة الرجاء؟
سأقوم بإغلاق هذه المشكلة لأننا حصلنا على تعليقات كافية وتم دمجها.
أسئلة وأجوبة شائعة: https://github.com/facebook/react/issues/14920#issuecomment -471070149
إذا كنت تريد الغوص العميق على useEffect
والاعتماديات ، فهي هنا: https://overreacted.io/a-complete-guide-to-useeffect/
سنضيف المزيد من الأشياء إلى المستندات قريبًا أيضًا.
إذا كنت تريد تغيير شيء ما في القاعدة أو لم تكن متأكدًا من أن قضيتك شرعية ، فقم بتقديم مشكلة جديدة.
التعليق الأكثر فائدة
لقد قمنا بتمرير هذه مع threepointone اليوم. هنا ملخص:
ثابت في قاعدة لينت
تبعيات غريبة
useEffect
لا تمنعك القاعدة من إضافة إدارات "خارجية" إلى
useEffect
بعد الآن نظرًا لوجود سيناريوهات شرعية.وظائف في نفس المكون لكنها معرفة خارج التأثير
لا يحذر Linter من الحالات التي يكون فيها الوضع آمنًا الآن ، ولكنه في جميع الحالات الأخرى يمنحك اقتراحات أفضل (مثل نقل الوظيفة داخل التأثير ، أو تغليفها بـ
useCallback
).يستحق الإصلاح في رمز المستخدم
إعادة ضبط الحالة على تغيير الدعائم
لا ينتج عن هذا انتهاكات الوبر بعد الآن ولكن الطريقة الاصطلاحية لإعادة ضبط الحالة استجابةً للدعائم مختلفة . سيحتوي هذا الحل على عرض غير متسق إضافي لذلك لست متأكدًا من أنه مرغوب فيه.
"القيمة غير الوظيفية الخاصة بي ثابتة"
تدفعك الخطافات نحو الصواب حيثما أمكن ذلك. إذا كنت تفعل تحديد DEPS (والتي في بعض الحالات يمكنك حذف)، ونحن نوصي بشدة لتشمل حتى تلك التي كنت تعتقد لن تتغير. نعم ، في هذا المثال
useDebounce
من غير المرجح أن يتغير التأخير. لكنه لا يزال يمثل خطأ إذا حدث ذلك ، لكن الخطاف لا يمكنه التعامل معه. يظهر هذا في سيناريوهات أخرى أيضًا. (على سبيل المثال ، تعتبر الخطافات أكثر توافقًا مع إعادة التحميل الساخن لأن كل قيمة يتم التعامل معها على أنها ديناميكية.)إذا كنت تصر تمامًا على أن قيمة معينة ثابتة ، فيمكنك فرضها.
الطريقة الأكثر أمانًا هي القيام بذلك بشكل صريح في واجهة برمجة التطبيقات الخاصة بك:
ثم من الواضح أنه لا يمكن تغييره ما لم تضعه داخل تصيير. (والذي لن يكون استخدامًا اصطلاحيًا للخطاف الخاص بك.) لكن القول بأن
<Slider min={50} />
لا يمكن تغييره أبدًا ليس صحيحًا حقًا - يمكن لأي شخص تغييره بسهولة إلى<Slider min={state ? 50 : 100} />
. في الواقع ، يمكن لشخص ما القيام بذلك:إذا قام شخص ما بتبديل
isCelsius
في الحالة ، فإن المكون الذي يفترض أنmin
لا يتغير أبدًا سيفشل في التحديث. ليس من الواضح في هذه الحالة أنSlider
سيكون هو نفسه (لكن سيكون ذلك لأنه يحتوي على نفس الموضع في الشجرة). إذن ، هذا مسدس قدم رئيسي من حيث إجراء تغييرات على الكود. تتمثل إحدى النقاط الرئيسية في React في أن التحديثات تُعرض تمامًا مثل الحالات الأولية (لا يمكنك عادةً تحديد أيها). سواء كنت ترسم قيمة الخاصية B ، أو انتقلت من قيمة الخاصية A إلى B - يجب أن تبدو وتتصرف بنفس الطريقة.في حين أن هذا غير مستحسن ، في بعض الحالات ، يمكن أن تكون آلية التنفيذ عبارة عن خطاف يحذر عندما تتغير القيمة (ولكنها توفر العنصر الأول). على الأقل بعد ذلك من المرجح أن يتم ملاحظته.
قد تكون هناك أيضًا حالة مشروعة حيث لا يمكنك التعامل مع التحديث. على سبيل المثال ، إذا كانت واجهة برمجة التطبيقات ذات المستوى الأدنى لا تدعمها ، مثل مكون jQuery الإضافي أو واجهة برمجة تطبيقات DOM. في هذه الحالة ، يظل التحذير مناسبًا حتى يفهمه مستهلك المكون الخاص بك. بدلاً من ذلك ، يمكنك إنشاء مكون غلاف يعيد تعيين
key
على التحديثات غير المتوافقة - مما يفرض إعادة التثبيت النظيف باستخدام الدعائم الجديدة. ربما يكون هذا هو الأفضل لمكونات الأوراق مثل المنزلقات أو مربعات الاختيار."قيمة وظيفتي ثابتة"
بادئ ذي بدء ، إذا كان ثابتًا ومرفوعًا إلى نطاق المستوى الأعلى ، فلن يشتكي linter. لكن هذا لا يساعد في الأمور التي تأتي من الدعائم أو السياق.
إذا كان حقا ثابتا ثم تحديد في DEPS لا يضر. مثل الحالة التي يتم فيها إرجاع دالة
setState
داخل خطاف مخصص إلى المكون الخاص بك ، ثم تقوم باستدعائها من أحد التأثيرات. قاعدة الوبر ليست ذكية بما يكفي لفهم المراوغة مثل هذا. ولكن من ناحية أخرى ، يمكن لأي شخص التفاف رد الاتصال هذا لاحقًا قبل العودة ، وربما يشير إلى خاصية أو حالة أخرى بداخلها. إذن لن يكون الأمر ثابتًا! وإذا فشلت في التعامل مع هذه التغييرات ، فستواجه أخطاء خاصية / حالة سيئة. لذا فإن تحديده يعد تقصيرًا أفضل.ومع ذلك ، فمن المفاهيم الخاطئة أن قيم الوظيفة ثابتة بالضرورة. غالبًا ما تكون ثابتة في الفئات بسبب طريقة الربط ، على الرغم من أن ذلك يخلق نطاقًا خاصًا من الأخطاء . لكن بشكل عام ، لا يمكن اعتبار أي دالة تغلق فوق قيمة في مكون دالة ثابتة. أصبحت قاعدة الوبر الآن أكثر ذكاءً في إخبارك بما يجب عليك فعله. (مثل تحريكه داخل التأثير - أبسط إصلاح - أو تغليفه بـ
useCallback
.)هناك مشكلة في الطيف المقابل لهذا ، حيث تحصل على حلقات لا نهائية (تتغير قيمة الوظيفة دائمًا). نلاحظ ذلك في قاعدة النسالة الآن عندما يكون ذلك ممكنًا (في نفس المكون) ونقترح إصلاحًا. لكن الأمر صعب إذا تجاوزت شيئًا ما بعدة مستويات.
لا يزال بإمكانك تغليفها بـ
useCallback
لإصلاح المشكلة. تذكر أنه من الناحية الفنية يمكن تغيير الوظيفة ، ولا يمكنك تجاهل هذه الحالة دون المخاطرة بالأخطاء. مثلonChange={shouldHandle ? handleChange : null}
أو عرضfoo ? <Page fetch={fetchComments /> : <Page fetch={fetchArticles />
في نفس المكان. أو حتىfetchComments
الذي يغلق فوق حالة المكون الأصلي. يمكن أن يتغير. مع الفئات ، سيتغير سلوكها بصمت لكن مرجع الوظيفة سيبقى كما هو. لذلك سيفتقد طفلك هذا التحديث - ليس لديك حقًا خيار خارج تمرير المزيد من البيانات إلى الطفل. مع مكونات الوظيفة وuseCallback
، تتغير هوية الوظيفة نفسها - ولكن فقط عند الضرورة. لذلك فهذه خاصية مفيدة وليست مجرد عائق يجب تجنبه.يجب أن نضيف حلاً أفضل لاكتشاف الحلقات غير المتزامنة اللانهائية. يجب أن يخفف هذا الجانب الأكثر إرباكًا. قد نضيف بعض الكشف عن هذا في المستقبل. يمكنك أيضًا كتابة شيء مثل هذا بنفسك:
هذا ليس مثاليًا وسنحتاج إلى التفكير في التعامل مع هذا بأمان أكثر. أوافق على أن مثل هذه الحالات سيئة للغاية. سيكون الإصلاح دون كسر القاعدة هو جعل
rules
ثابتًا ، على سبيل المثال عن طريق تغيير API إلىcreateTextInput(rules)
، ولفregister
وunregister
إلىuseCallback
. والأفضل من ذلك ، قم بإزالةregister
وunregister
، واستبدلها بسياق منفصل حيث تضعdispatch
بمفردها. ثم يمكنك أن تضمن أنك لن تحصل على هوية دالة مختلفة عن قراءتها.أود أن أضيف أنك ربما تريد وضع
useMemo
على قيمة السياق على أي حال لأن الموفر يقوم بالكثير من العمليات الحسابية التي سيكون من المحزن تكرارها إذا لم يتم تسجيل مكونات جديدة ولكن تم تحديث الأصل الخاص به. لذا فإن هذا النوع من الأمور يضع المشكلة التي ربما لم تلاحظها بطريقة أخرى أكثر وضوحًا. على الرغم من أنني أوافق على أننا بحاجة إلى جعلها أكثر بروزًا عند حدوث ذلك.يؤدي تجاهل تبعيات الوظائف تمامًا إلى أخطاء أسوأ في مكونات الوظيفة والخطافات لأنها ستستمر في رؤية الدعائم التي لا معنى لها وتذكر إذا قمت بذلك. لذا حاول ألا تفعل ذلك عندما تستطيع.
الرد على تغييرات القيمة المركبة
إنه أمر غريب بالنسبة لي لماذا يستخدم هذا المثال تأثيرًا لشيء يعد في الأساس معالج حدث. إن القيام بنفس "السجل" (أفترض أنه يمكن أن يكون نموذج إرسال) في معالج الحدث يبدو أكثر ملاءمة. هذا صحيح بشكل خاص إذا فكرنا في ما يحدث عند فك المكون. ماذا لو تم فكها مباشرة بعد جدولة التأثير؟ أشياء مثل إرسال النموذج لا يجب أن "تحدث" فقط في هذه الحالة. لذلك يبدو أن التأثير قد يكون خيارًا خاطئًا هناك.
ومع ذلك ، لا يزال بإمكانك فعل ما جربته - بجعل
fullName
يكونsetSubmittedData({firstName, lastName})
بدلاً من ذلك ، ثم[submittedData]
هو التبعية الخاصة بك ، والتي يمكنك من خلالها قراءةfirstName
وlastName
.التكامل مع التعليمات البرمجية الحتمية / القديمة
عند الدمج مع أشياء ضرورية مثل مكونات jQuery الإضافية أو واجهات برمجة تطبيقات DOM الخام ، قد يُتوقع بعض السذاجة. ومع ذلك ، ما زلت أتوقع منك أن تكون قادرًا على دمج التأثيرات أكثر قليلاً في هذا المثال.
أتمنى ألا أنسى أحداً! اسمحوا لي أن أعرف إذا فعلت أو إذا كان هناك شيء غير واضح. سنحاول تحويل الدروس من هذا إلى بعض المستندات قريبًا.