React: [ESLint] تعليقات حول قاعدة الوبر "الشاملة"

تم إنشاؤها على ٢١ فبراير ٢٠١٩  ·  111تعليقات  ·  مصدر: facebook/react

الإجابات الشائعة

💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡

قمنا بتحليل التعليقات على هذا المنشور لتقديم بعض الإرشادات: https://github.com/facebook/react/issues/14920#issuecomment -471070149.

💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡


ما هذا

هذه قاعدة ESLint جديدة تتحقق من قائمة تبعيات الخطافات مثل useEffect وما شابهها ، تحمي من مخاطر الإغلاق التي لا معنى لها. بالنسبة لمعظم الحالات ، يحتوي على إصلاح تلقائي. سنضيف المزيد من الوثائق خلال الأسابيع القادمة.

autofix demo

التركيب

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 لكنك تعتقد أن شفرتك صحيحة ، يرجى النشر في هذه المشكلة.


قبل أن تنشر أي تعليق

يرجى تضمين هذه الأشياء الثلاثة:

  1. رمز CodeSandbox يوضح مثالًا برمجيًا صغيرًا لا يزال يعبر عن نيتك (ليس "شريط foo" ولكن نمط واجهة المستخدم الفعلي الذي تقوم بتنفيذه).
  2. شرح للخطوات التي يقوم بها المستخدم وما تتوقع رؤيته على الشاشة.
  3. شرح لواجهة برمجة التطبيقات (API) المقصودة للخطاف / المكون الخاص بك.

please

لكن حالتي بسيطة ، لا أريد تضمين هذه الأشياء!

قد يكون الأمر بسيطًا بالنسبة لك - ولكنه ليس بسيطًا على الإطلاق بالنسبة لنا. إذا كان تعليقك لا يتضمن أيًا منهما (على سبيل المثال ، لا يوجد رابط CodeSandbox) ، فسنخفي تعليقك لأنه من الصعب جدًا تتبع المناقشة بخلاف ذلك. شكرًا لك على احترام وقت الجميع من خلال تضمينهم.

الهدف النهائي من هذا الموضوع هو العثور على سيناريوهات مشتركة وتحويلها إلى مستندات وتحذيرات أفضل. يمكن أن يحدث هذا فقط عندما تتوفر تفاصيل كافية. تؤدي التعليقات التي تتضمن مقتطفات غير مكتملة من التعليمات البرمجية إلى خفض جودة المناقشة بشكل كبير - لدرجة أنها لا تستحق ذلك.

ESLint Rules Discussion

التعليق الأكثر فائدة

لقد قمنا بتمرير هذه مع 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 الخام ، قد يُتوقع بعض السذاجة. ومع ذلك ، ما زلت أتوقع منك أن تكون قادرًا على دمج التأثيرات أكثر قليلاً في هذا المثال.


أتمنى ألا أنسى أحداً! اسمحوا لي أن أعرف إذا فعلت أو إذا كان هناك شيء غير واضح. سنحاول تحويل الدروس من هذا إلى بعض المستندات قريبًا.

ال 111 كومينتر

هذا المثال له رد: 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] .

  1. رمز CodeSandbox يوضح مثالًا برمجيًا صغيرًا لا يزال يعبر عن نيتك (ليس "شريط foo" ولكن نمط واجهة المستخدم الفعلي الذي تقوم بتنفيذه).

https://codesandbox.io/s/xpr69pllmz

  1. شرح للخطوات التي يقوم بها المستخدم وما تتوقع رؤيته على الشاشة.

سيضيف المستخدم بريدًا إلكترونيًا ويدعو هؤلاء البريد الإلكتروني إلى التطبيق. أزلت عن قصد بقية واجهة المستخدم إلى button لأنها ليست ذات صلة بمشكلتي.

  1. شرح لواجهة برمجة التطبيقات (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 أقدر ملاحظاتك ولكني أخفيت تعليقاتك لأنها لم تتضمن المعلومات التي طلبناها في الجزء العلوي من المشكلة. هذا يجعل هذه المناقشة صعبة على الجميع بلا داع لأن هناك سياق مفقود. سأكون سعيدًا لمواصلة المحادثة إذا نشرت مرة أخرى وقمت بتضمين جميع المعلومات ذات الصلة (انظر المنشور العلوي). شكرا لتفهمك.

  1. كود ساندبوكس
  2. يمكن للمستخدم تحديد الاسم الأول ويفرض تغيير الاسم الأخير. يمكن للمستخدم تحديد اسم العائلة ولا يفرض تغيير الاسم الأول.
  3. هناك خطاف مخصص يقوم بإرجاع جزء من الحالة ، ومكون محدد يقوم بتحديث هذه الحالة ، وطريقة تحديث خطاف الحالة التي تقوم بتحديث الحالة برمجيًا. كما هو موضح ، لا أرغب دائمًا في استخدام وظيفة التحديث ، لذا تركتها لتكون العنصر الأخير في المصفوفة المرتجعة.

أعتقد أن الكود كما هو لا يجب أن يخطئ. إنها تعمل الآن في السطر 35 ، قائلة إنه يجب تضمين setLastName في المصفوفة. أي أفكار حول ما يجب القيام به حيال ذلك؟ هل أفعل شيئًا غير متوقع؟

أنا أفهم تمامًا ، وعادة ما أفعل كل ذلك من أجلك ، ولكن في حالتي المحددة ، لا يعتبر أي من الكود فريدًا بالنسبة لي. إنه مجرد سؤال حول ما إذا كان استخدام وظيفة تم تعريفها خارج الخطاف (أي منشئ إجراء إعادة الإرسال) والمستخدمة داخل الخطاف يجب أن تضاف إلى هذه الوظيفة باعتبارها نقطة ربط.

يسعدنا إنشاء رموز وصندوق إذا كنت لا تزال بحاجة إلى مزيد من المعلومات. شكرا

joelmoss أعتقد أن

نعم ، لا يزال CodeSandbox مفيدًا. يرجى تخيل ما يشبه تبديل السياق بين مقتطفات التعليمات البرمجية للأشخاص طوال اليوم. إنها خسارة ذهنية هائلة. لا أحد منهم يبدو متشابهًا. عندما تحتاج إلى تذكر كيفية استخدام الأشخاص لمنشئي الإجراءات في Redux أو بعض المفاهيم الأخرى الخارجية لـ React ، يكون الأمر أكثر صعوبة. قد تبدو المشكلة واضحة لك ولكن ليس واضحًا بالنسبة لي ما قصدته.

gaearon أفهم أنه من المنطقي ، لقد رغبتي في تحقيقه كان:

إذا كنت تريد تشغيل تأثير وتنظيفه مرة واحدة فقط (على mount and unmount) ، يمكنك تمرير مصفوفة فارغة ([]) كوسيطة ثانية.

شكرا جزيلا للتوضيح. (وآسف على الموضوع الخارج)

ها هي نسخة الكود لما أقوم بتطبيقه. لا يبدو كثيرًا ، لكن النمط مطابق بنسبة 100٪ مع الكود الحقيقي الخاص بي.

https://codesandbox.io/s/2x4q9rzwmp؟fontsize=14

  • شرح للخطوات التي يقوم بها المستخدم وما تتوقع رؤيته على الشاشة.

كل شيء يعمل بشكل رائع في هذا المثال! باستثناء قضية لينتر.

  • شرح لواجهة برمجة التطبيقات (API) المقصودة للخطاف / المكون الخاص بك.

لدي العديد من الملفات مثل هذا ، حيث أقوم بتنفيذ وظيفة في التأثير ، لكنني أريد تشغيلها فقط عندما يتم استيفاء بعض الشروط - على سبيل المثال ، تغيير معرف السلسلة. لا أريد تضمين الوظيفة في المصفوفة.

هذا ما أحصل عليه من 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 لمنع إعادة تحميل الصفحة.

كل شيء يعمل!

  • المقصود API

لقد وضعت التبعيات بشكل صحيح كما هو الحال في صندوق حماية الكود ، لكن الوتر يخبرني أن قائمة التبعيات يجب أن تحتوي على 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 عند كل ضغطة مفتاح.

إن إضافة IMO "كل شيء" نستخدمه في مصفوفة التبعيات ليست فعالة.

على سبيل المثال ، ضع في اعتبارك استخدام الخطاف useDebounce . في استخدامات العالم الحقيقي (على الأقل في استخداماتنا) ، لن يتغير delay بعد أول تصيير ، لكننا نتحقق مما إذا كان قد تم تغييره أم لا في كل إعادة تصيير. لذلك ، في هذا الخطاف ، من الأفضل أن تكون الوسيطة الثانية لـ useEffect هي [value] بدلاً من [value, delay] .

من فضلك لا تعتقد أن عمليات التحقق من المساواة الضحلة رخيصة للغاية. يمكن أن تساعد تطبيقك عند وضعه بشكل استراتيجي ولكن مجرد جعل كل مكون نقيًا يمكن أن يجعل تطبيقك في الواقع أبطأ. المفاضلات.

أعتقد أن إضافة كل شيء في مصفوفة التبعيات ، له نفس مشكلة الأداء (أو حتى أسوأ) مثل استخدام المكونات النقية في كل مكان. نظرًا لوجود العديد من السيناريوهات التي نعلم فيها أن بعض القيم التي نستخدمها لن تتغير ، لذلك لا ينبغي أن نضيفها في مصفوفة التبعيات ، لأنه كما قال gaearon ، عمليات التحقق من المساواة الضحلة ليست رخيصة جدًا وهذا يمكن جعل تطبيقنا أبطأ.

لدي بعض التعليقات حول تمكين هذه القاعدة للعمل تلقائيًا على الخطافات المخصصة حسب الاتفاقية.

أستطيع أن أرى في الكود المصدري أن هناك نية ما للسماح للأشخاص بتحديد regex لالتقاط الخطافات المخصصة بالاسم:

https://github.com/facebook/react/blob/ba708fa79b3db6481b7886f9fdb6bb776d0c2fb9/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L490 -L492

ما الذي قد يفكر فيه فريق 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.

screen shot 2019-02-25 at 11 06 40 am

لا أعتقد أن linter يجب أن يقدم هذا الاقتراح لأنه يغير طريقة عمل التطبيق.

  1. CodeSandbox: https://codesandbox.io/s/ol6r9plkv5
  2. الخطوات: عند parent التغييرات، child يعيد إلى الصفر.
  3. النية: بدلاً من وضع علامة على التبعية على أنها غير ضرورية ، يجب أن يدرك linter أن 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 لا يزال لا يعمل في حالة أن الإجراء يأتي من الدعامة. ضع في اعتبارك هذا المثال:

image

حيث يكون setScrollTop إجراء إعادة.

في هذا المثال في المكون Slider ، أستخدم useEffect للانتظار حتى يتوفر DOM حتى يمكنني تحميل مكون noUiSlider. لذلك قمت بتمرير [sliderElement] للتأكد من أن المرجع متاح في DOM عند تشغيل التأثير. نقوم أيضًا بعرض مكوناتنا على الخادم ، لذلك يضمن هذا أيضًا توفر DOM قبل العرض. الدعائم الأخرى التي أستخدمها في useEffect (أي min ، max ، onUpdate ، إلخ) هي ثوابت ، وبالتالي لا أرى حاجة لتمريرها إلى التأثير.

screen shot 2019-03-02 at 5 17 09 pm


هذا هو التأثير كما هو موضح في 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 فلا ينبغي أن تكون جزءًا من تبعياتها (صححني إذا كنت مخطئًا).

  1. كود ساندبوكس
  2. خطوات:
    ينقر المستخدم داخل المربع لتنشيطه ، وفي الخارج لجعله غير نشط ، يمكن للمستخدم أيضًا النقر بزر الماوس الأيمن ، على الرغم من النقرة الأولى ، لا يجب تشغيل قائمة السياق ( e.preventDefault ).
    عندما أقول "أول نقرة" أعني النقرة الأولى التي تغير الحالة.
    بالنظر إلى المربع النشط ، فإن النقر بزر الماوس الأيمن خارجه سيغير الحالة إلى "غير نشط" ويمنع قائمة السياق. لن تؤثر نقرة أخرى خارجية على الحالة ، ومن ثم يجب أن تظهر قائمة السياق.

آمل أن أكون واضحًا هنا وأن حالة الاستخدام مفهومة ، يرجى إعلامي إذا كنت بحاجة إلى تقديم مزيد من المعلومات. شكرا!

مرحبًا gaearon ، أقوم بإضافة اقترحتني في Twitter! أجد صعوبة في العثور على الشكل الصحيح الذي لا يتخطى التبعيات.

إنه مفصل كمثال ، آمل أن أتمكن من شرحه بطريقة واضحة ومفهومة.

هذه هي الحالة الحالية لها: رد فعل غير متزامن-utils / src / hooks / useAsyncData.ts

نظرة عامة على الوظيفة
مساعدة المستخدم في التعامل مع المكالمات غير المتزامنة والبيانات الناتجة وحالتها طوال العملية.

triggerAsyncData التحديثات asyncData state بشكل غير متزامن وفقًا للدالة getData التي تُرجع Promise . يمكن استدعاء triggerAsyncData كتأثير أو "يدويًا" بواسطة مستخدم الخطاف.

التحديات

  1. تبعيات التأثير الذي يستدعي triggerAsyncData هي تلك التبعيات الضمنية. triggerAsyncData هو تبعية للتأثير ، ولكن يتم إنشاؤه في كل تصيير. خط الأفكار حتى الآن:

    1. ما عليك سوى إضافتها على أنها تبعية => ولكن بعد ذلك يتم تشغيل التأثير في كل عملية تصيير.

    2. قم بإضافتها كتبعية ، ولكن استخدم useMemo / useCallback مع triggerAsyncData => يجب استخدام useMemo / useCallback فقط لتحسين الأداء بقدر ما أعلم.

    3. ضع نطاقًا داخل التأثير => ثم لا يمكنني إعادته إلى المستخدم.

    4. بدلاً من استخدام triggerAsyncData كتبعية ، استخدم تبعيات triggerAsyncData كاعتماديات => أفضل خيار وجدته حتى الآن. لكنها تخرق قاعدة "الاستنفاد".

  2. كل معلمة إدخال للخطاف المخصص هي / تصبح تبعية لتأثيرنا الداخلي. لذا فإن الدالات المضمنة والكائنات الحرفية كمعلمات تجعل التأثير يعمل كثيرًا.

    1. اترك للمستخدم المسؤولية. سيقدمون القيم المناسبة ، باستخدام useMemo / useCallback إذا لزم الأمر => أخشى أنهم لن يفعلوا ذلك في كثير من الأحيان. وإذا فعلوا ذلك فهو مطوّل تمامًا.

    2. اسمح بحجة إضافية للخطاف المخصص لتوفير تفاصيل المدخلات ، واستخدمها بدلاً من المدخلات نفسها => بارد ، أقل إسهابًا ، المزيد من التحكم للمستخدم. أنا أستخدم هذا. لكنها تخرق قاعدة "الاستنفاد". (في الواقع باستخدام أنا هذا وأعود إلى التقسيمات العادية إذا لم يتم توفير حجة إضافية. أجدها نمطًا قويًا).

  3. تؤدي التبعيات التي تتم إدارتها بشكل سيئ للخطاف المخصص إلى إنشاء حلقة غير متزامنة لانهائية بسبب التأثير الداخلي. لقد استخدمت البرمجة الدفاعية لمنع ذلك ، لكنها تضيف "وهمية"؟ التبعية ( asyncData ). هذا يكسر قاعدة "استنفاد" مرة أخرى.

شرح أطول مما كنت أتمنى ... لكنني أعتقد أنه يعكس كفاحي لاستخدام الخطافات بطريقة مناسبة. يرجى إعلامي إذا كان هناك شيء آخر يمكنني القيام به لتوضيح هذه الصعوبات.

شكرا لك على كل العمل الشاق هنا!

يا gaearon شكرا على كل ما تبذلونه من العمل الشاق.

  1. الحد الأدنى من البيانات غير المتزامنة في جلب مثال على مثال CodeSandbox .

  2. من المتوقع أن يرى المستخدم 5 سلاسل عنوان lorem ipsum تم جلبها من

  3. لقد أنشأت خطافًا مخصصًا لجلب البيانات باستخدام واجهة برمجة التطبيقات (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:

demo

أتمنى أن تجربها في مشاريعك وترى كيف تشعر! (يرجى التعليق على هذا التدفق المحدد في 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 لتوضيح المشكلة.

مثال على 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. مركب ، افعل شيئًا باستخدام دعامة
  2. تم تحديث المكون (على سبيل المثال: تم تغيير قيمة الخاصية) ، افعل شيئًا مع قيمة الخاصية الجديدة

الرقم 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/

سنضيف المزيد من الأشياء إلى المستندات قريبًا أيضًا.

إذا كنت تريد تغيير شيء ما في القاعدة أو لم تكن متأكدًا من أن قضيتك شرعية ، فقم بتقديم مشكلة جديدة.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات