React: الخطأ: من الصعب جدًا إصلاحه "لا يمكن تحديث مكون من داخل جسم الوظيفة لمكون مختلف."

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

# ملاحظة: قامت React 16.13.1 بإصلاح بعض الحالات التي يكون فيها هذا الأمر مفرطًا. إذا لم تؤد ترقية React و ReactDOM إلى 16.13.1 إلى إصلاح التحذير ، فاقرأ هذا: https://github.com/facebook/react/issues/18178#issuecomment -595846312

نسخة رد الفعل:

16.13.0

خطوات التكاثر

  1. بناء آلة الزمن.
  2. اذهب إلى عام 2017.
  3. أنشئ تطبيقًا ضخمًا يتكون من 10 آلاف سطر من التعليمات البرمجية.
  4. احصل على 80 تبعيات (!) في ملف package.json بما في ذلك تلك التي لم تعد محفوظة.
  5. تحديث تفاعل إلى أحدث إصدار في 27 فبراير 2020.
  6. احصل على الكثير من الأخطاء التي لا تعرف كيفية إصلاحها.
  7. أخبر عميلك أن الإصلاحات ستستغرق وقتًا غير معروف وستكلف $ $ + أيام أو أسابيع من التحقيق أو سنعلق بالإصدار القديم من React والمكتبات ذات الصلة إلى الأبد والتي ستكلف أكثر من $ $ ولكن في وقت لاحق.

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

مجرد مثال واحد:

image

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

الرجاء عمل خيار "غير آمن" لتعطيل هذا الخطأ أو توفير طريقة أبسط للعثور على مكان الخطأ 🙏

Discussion

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

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

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

شكرا لكم جميعا.

ال 109 كومينتر

أتمنى لو أضفنا هذا التحذير في وقت سابق. أنا آسف لم نفعل. لقد كان سهواً على إدخال الخطافات. أعتقد أن السبب في ذلك هو الكود الأحدث الذي يستخدم الخطافات ، لأنه كان هناك بالفعل نفس التحذير للفئات قبل ذلك بكثير ، لذا فإن أي كود سابق قد رأى هذا التحذير بالفعل.

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

ومع ذلك ، بعد قولي هذا ، نريد مساعدتك وتسهيل إصلاحها قدر الإمكان ، بما في ذلك معرفة ما إذا كان بإمكاننا الحصول على مساعدة من مؤلفي المكتبة لنشر إصدارات ثابتة.

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

يظهر هذا معي عندما أستخدم فورميك

image

sebmarkbage أشكركم على الرد. يعد تتبع المكدس الذي يظهر بعد النقر فوق> أمرًا مثيرًا للسخرية. يحتوي على أكثر من 200 عنصر!

كنت سأقوم بلصقه هناك أو إعطاء رابط لـ pastebin لكنني جربت اتجاهًا مختلفًا. مشيت عبر مشكلات Github لبعض المكتبات المستخدمة واكتشفت أن أحد المشتبه بهم هو نموذج إعادة التشغيل: https://github.com/redux-form/redux-form/issues/4619. آمل أن تكون المكتبة الوحيدة التي تسبب الأخطاء وسأنتظر الإصلاح قبل ترقية React.

لكن مع ذلك ، أطلب عدم إغلاق هذه المشكلة وأقترح مطورين آخرين أن يذكروا هنا المكتبات التي تسبب أيضًا هذه الأخطاء.

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

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

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

في هذه الحالة ، يكون السطر الأول الذي يجب البحث عنه هو السطر بعد dispatchAction . يجب أن يكون هذا هو الشيء الذي يستدعي React.

RodolfoSilva هل يمكنك نشر مصدر FormItemInput.js ، إذا كان شيئًا يمكنك مشاركته؟ يبدو أن هذا يستدعي الإرسال أو setState على السطر 71.

أعتقد أنه من الضروري تعديل رسالة الخطأ هذه لتشمل بالضبط أي سطر من الكود يسبب الخطأ. يكاد يكون من المستحيل تحديد ما إذا كان سبب المشكلة هو رمز محلي أو رمز مكتبة. مكتبات librairies الخارجية

أنا متأكد تمامًا من أن React-Redux ليست مخطئة هنا ، لكنني أوافق على أن رسالة الخطأ ومكدس المكونات تجعل من الصعب نوعًا ما معرفة ما يحدث بالفعل.

أواجه نفس المشكلة مع Redux-form!

لدي نفس المشكلة وأرى أن التحذير يظهر في المرة الأولى التي أكتب فيها في حقل إعادة الإرسال الخاص بي أو عندما بالكامل

لدي هذه المشكلة أيضًا ، هذا هو المكون الخاص بي:

`const [price، setPrice] = useState (0) ؛

const updatePrice = (newPrice) => {
setPrice (سعر جديد)
}
<CardContainer onPriceUpdated = {updatePrice}> CardContainer>
"

لذلك ، في هذه الحالة ، مكون CardContainer الخاص بي ، قم بإخطار المكون الرئيسي عند تحديث السعر وعرض المكون الرئيسي الأمير الجديد.
لذا أعتقد أن React تحذرني من أنني أحاول تحديث حالة أحد المكونات باستخدام وظيفة مكون آخر.
أنا جديد في رد الفعل ، لذلك لست متأكدًا مما إذا كان هذا نمط React أم أنه خطأ في الواقع.

إذا كان لديكم أي اقتراح لحل هذا التحذير ، فسأكون ممتنًا ''

تضمين التغريدة

أعتقد أنه من الضروري تعديل رسالة الخطأ هذه لتشمل بالضبط أي سطر من الكود يسبب الخطأ.

هناك حدود لما يمكننا فعله في JavaScript. لكن جميع المعلومات موجودة في المكدس التي تراها في المتصفح . كل ما عليك فعله هو تخطي الأسطر الموجودة داخل React.

لرؤية مكدس JavaScript ، تحتاج إلى النقر فوق سهم صغير بجوار رسالة الخطأ .

على سبيل المثال ، انظر إلى لقطة الشاشة هذه من وقت سابق:

75614021-cb812980-5b12-11ea-8a6e-a38f4cd6aeef

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

martinezwilmer هذا المثال صغير جدًا للمساعدة. قم بإنشاء صندوق رمل من فضلك.

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

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

شكرا لكم جميعا.

من الصعب العثور على السطر الدقيق المعني بالتحذير هنا:

gaearon هل لديك مرة أخرى نصيحة واحدة حول هذا الموضوع؟

image

من الصعب العثور على السطر الدقيق المعني بالتحذير هنا:

ما الذي يجعل الأمر صعبًا؟ كما أشرت أعلاه ، فإن السطر الأول لا يقول "رد فعل" على الجانب الأيمن. في هذه الحالة ، إنه connectAdvanced من Redux. يُرجى تقديم مشكلة في React Redux حتى يتسنى للمشرفين النظر في هذا الأمر.

كما قلت لأعلى ، سأكون مندهشًا جدًا إذا كان كل ما يحدث هنا يمثل مشكلة في React-Redux.

بعد قولي هذا ، لست متأكدًا أيضًا من سبب إطلاق هذه الرسالة في المقام الأول. لقد فهمت ما تقوله رسالة الخطأ ، ولكن ما نوع نمط رمز التطبيق الذي سيكون في الواقع مثالًا على هذا السلوك؟

واجهت هذا مؤخرًا وكان الإصلاح يلف مواقع مكالمات معالج setState useEffect ، مثل: https://github.com/airbnb/lunar/commit/db08613d46ea21089ead3e7b5cfff995f15c69a7#diff -1c3bdd397b1ce506424 onChange و onSubmit استخدم setState أعلى السلسلة).

martinezwilmer أين يتم onPriceUpdated ؟ ربما حاول تغليفها بـ useEffect ؟

يبدو أن نفس المشكلة تحدث مقابل urql

نحن نستخدم use-subscription + وونكا (للتدفقات) لتنظيم تحديثاتنا ، ومع ذلك يمكن أن يأتي التحديث بشكل متزامن. هنا قمنا بالفعل بإحضار todos لذا إذا نقرنا على الزر Open ، يجب أن تظهر النتيجة على الفور ، ولكن يبدو أن هذا يؤدي إلى حدوث الخطأ التالي.

image

في حالتنا هذا هو المقصود ، لا يمكننا فقط إظهار fetching: true لنتيجة مزامنة ، مما قد ينتج عنه واجهات سريعة.

بدأ هذا في الظهور أكثر فأكثر في مكتبات الطرف الثالث الآن: urql ، Apollo .

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

شيء واحد ألاحظه من قضية Apollo هو:

يُظهر تتبع تكديس التحذير المكون الذي بدأ التغييرات ، وليس المكون الذي تمت إعادة تصييره [كذا] من خلال تلك التغييرات

إذا كان هذا صحيحًا ، فهل يمكن لـ React تقديم مزيد من المعلومات هنا؟ ربما تخبرنا عن كل من مكون البدء والمكونات التي تسببت في تحديثها؟

مثل hugo ، واجهت هذا عند اختبار تطبيق Ionic جديد :

  1. npx ionic start demo sidemenu --type=react
  2. react-scripts test

في الواقع ، السبب مدفون في منتصف وأسفل أثر المكدس.

console.error node_modules/react-dom/cjs/react-dom.development.js:88
    Warning: Cannot update a component from inside the function body of a different component.
        in Route (at App.tsx:37)
        in View (created by StackManagerInner)
        in ViewTransitionManager (created by StackManagerInner)
        in ion-router-outlet (created by IonRouterOutlet)
        in IonRouterOutlet (created by ForwardRef(IonRouterOutlet))
        in ForwardRef(IonRouterOutlet) (created by StackManagerInner)
        in StackManagerInner (created by Context.Consumer)
        in Unknown (created by Component)
        in Component (created by ForwardRef(IonRouterOutlet))
        in ForwardRef(IonRouterOutlet) (at App.tsx:36)
        in ion-split-pane (created by IonSplitPane)
        in IonSplitPane (created by ForwardRef(IonSplitPane))
        in ForwardRef(IonSplitPane) (at App.tsx:34)
        in NavManager (created by RouteManager)
        in RouteManager (created by Context.Consumer)
        in RouteManager (created by IonReactRouter)
        in Router (created by BrowserRouter)
        in BrowserRouter (created by IonReactRouter)
        in IonReactRouter (at App.tsx:33)
        in ion-app (created by IonApp)
        in IonApp (created by ForwardRef(IonApp))
        in ForwardRef(IonApp) (at App.tsx:32)
        in App (at App.test.tsx:6)

كانت هذه المشكلة أقرب ما يمكن أن أجده فيما يتعلق بهذه المشكلة.

وجدنا نمطًا محددًا يسبب هذه المشكلة مع mobx في https://github.com/mobxjs/mobx-react/issues/846

sebmarkbage لم يعد بإمكاني إعادة إنتاج هذه المشكلة. لقد قمنا بتحديث بعض المكتبات وتجاوزت المشاكل.

jgoux يبدو أننا نواجه نفس المشكلة كلوفيس ^ ^ مرقط

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

شكرًا gaearon على التفاصيل حول كيفية التصحيح من المكدس ، فأنا شخصياً لم أكن

لست متأكدًا مما إذا كانت مشكلتي مرتبطة أم لا ، فأنا أحاول تحديث حالة مكون النموذج الخاص بي ، ولكن عندما أحاول إضافة معالج onChange ، فإنه يستمر في إعطائي هذا الخطأ. ضع في اعتبارك أنني أستخدم نموذج رد فعل jsonschema وقمت باستيراد مكون النموذج وأنا أستخدمه onChange لتحديث الحالة ..

بالنسبة لي ، هذا هو النمط الذي يسبب المشكلة.

image

قد يكون هناك طريقة للتغلب على هذا. لكن سجل وحدة التحكم وجهني مباشرة إلى السطر 385

أنا جديد على الرد ولكن كان لدي رمز مثل هذا:

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={setMobileNavOpen(false)} //problem here
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

مما أدى إلى: Cannot update a component from inside the function body of a different component.

كل ما كان علي فعله هو إضافة وظيفة سهم لتعيين MobileNavOpen في MobileNav مثل:

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(false)} //fixes problem
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

وهذا يحل المشكلة ، أتمنى أن يساعد هذا شخصًا ما!

أنا جديد على الرد ولكن كان لدي رمز مثل هذا:

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={setMobileNavOpen(false)} //problem here
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

مما أدى إلى: Cannot update a component from inside the function body of a different component.

كل ما كان علي فعله هو إضافة وظيفة سهم لتعيين MobileNavOpen في MobileNav مثل:

import React, { useState } from "react";

function Home() {
  const [mobileNavOpen, setMobileNavOpen] = useState(false);
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(true)}
        type="button"
        className="btn"
      >
        X
      </button>
      {mobileNavOpen && <MobileNav setMobileNavOpen={setMobileNavOpen}/>}
    </div>
  );
}

function MobileNav() {
  return (
    <div>
      <button
        onClick={(): void => setMobileNavOpen(false)} //fixes problem
        type="button"
        className="btn"
      >
        X
      </button>
    </div>
  );
}

export default Home;

وهذا يحل المشكلة ، أتمنى أن يساعد هذا شخصًا ما!

مثالك هو في الواقع أحد الأخطاء المبكرة التي يرتكبها الناس عند التفاعل. لست متأكدًا من أنها نفس المشكلة التي تتم مناقشتها هنا. الخط الخاص بك هنا: onClick={setMobileNavOpen(false) يستدعي الوظيفة أثناء عطاء الزر ، وليس عند النقر. هذا هو السبب في أن لفها في دالة السهم يصلحها.

واجهت هذه المشكلة مع React Router حيث احتجت إلى إرسال إجراء Redux قبل <Redirect> ing المستخدم إلى موقع مختلف. يبدو أن المشكلة هي أن إعادة التوجيه حدثت قبل انتهاء الإرسال. لقد أصلحت المشكلة من خلال التعهد بفعلي.

قبل:

<Route
  render={routeProps => {
    setRedirectionTarget(somePath(routeProps));
    return <Redirect to={someOtherPath} />;
  }}
/>;

function mapDispatchToProps(dispatch: ThunkDispatch) {
  return {
    setRedirectionTarget: (target: string | null) => dispatch(setRedirectionTarget(target))
  };
}

export const setRedirectionTarget = (location: string | null): SetRedirectionTarget => {
  return {
    type: SET_REDIRECTION_TARGET,
    location
  };
};

بعد، بعدما:

function mapDispatchToProps(dispatch: ThunkDispatch) {
  return {
    setRedirectionTarget: async (target: string | null) => dispatch(await setRedirectionTarget(target))
  };
}

export const setRedirectionTarget = (location: string | null): Promise<SetRedirectionTarget> => {
  return Promise.resolve({
    type: SET_REDIRECTION_TARGET,
    location
  });
};

أعتقد أنه من الممكن جعل رسالة الخطأ تتضمن أسماء لكل من المكون الذي يتم عرضه حاليًا ، والمكون الذي يتم استدعاء setState الخاص به. هل يريد أي شخص أن يرسل PR لهذا الغرض؟

أعتقد أنه من الممكن جعل رسالة الخطأ تتضمن أسماء لكل من المكون الذي يتم عرضه حاليًا ، والمكون الذي يتم استدعاء setState الخاص به. هل يريد أي شخص أن يرسل PR لهذا الغرض؟

أنا سعيد لإلقاء نظرة على هذا. أي مؤشرات يجب أن أكون على علم بها؟

@ samcooke98 لقد فتحت https://github.com/facebook/react/pull/18316

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

export const useOrderbookSubscription = marketId => {
  const { data, error, loading } = useSubscription(ORDERBOOK_SUBSCRIPTION, {
    variables: {
      marketId,
    },
  })

  const formattedData = useMemo(() => {
    // Don't dispatch in here.
  }, [data])

  // Don't dispatch here either

  // Dispatch in a useEffect
  useEffect(() => {
    orderbookStore.dispatch(setOrderbookData(formattedData))
  }, [formattedData])

  return { data: formattedData, error, loading }
}

كنا نواجه هذه المشكلة في Hyper لكننا لا نستخدم الخطافات ، ولم نتمكن من العثور على أي شيء في استدعاء استدعاء من مكدس الاستدعاءات. ولكن كانت هناك مكالمة في UNSAFE_componentWillReceiveProps في المكدس. تحديث ذلك بـ componentDidUpdate أصلح المشكلة لنا https://github.com/zeit/hyper/pull/4382
نشره هنا إذا كان من الممكن أن يساعد شخص ما

بالمثل هنا ، كان هناك استدعاء UNSAFE_componentWillMount ، تغيير / إزالة حل المشكلة

لكننا لا نستخدم الخطافات ، ولم نتمكن من العثور على أي شيء في استدعاء استدعاء من مكدس الاستدعاءات

هذا يبدو غريبا. لست متأكدًا من كيفية تلقيك هذا التحذير بعد ذلك. يتم تنشيطه فقط إذا كانت setState تنتمي إلى مكون دالة. ما هو شكل المكدس الخاص بك؟

لكننا لا نستخدم الخطافات ، ولم نتمكن من العثور على أي شيء في استدعاء استدعاء من مكدس الاستدعاءات

هذا يبدو غريبا. لست متأكدًا من كيفية تلقيك هذا التحذير بعد ذلك. يتم تنشيطه فقط إذا كانت setState تنتمي إلى مكون دالة. ما هو شكل المكدس الخاص بك؟

صنف المكونات مع Redux والوظائف التي تطبق الإضافات على المكونات. ربما لهذا السبب يتم احتسابها كعنصر وظيفة؟ ولكن لماذا إذن يؤدي تحديث خطافات دورة الحياة إلى إصلاحها؟

لست متأكد. هل يمكنك محاولة إنشاء مثال منعزل في وضع الحماية؟ لدي حدس أنك ربما تفعل شيئًا آخر غير متوقع.

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

هل يمكن أن يكون ذلك بسبب حقيقة أن كلا من UNSAFE_componentWillReceiveProps و componentDidUpdate كانا في المكون في ذلك الوقت (تم ترك عنصر غير آمن هناك عن طريق الخطأ)؟

أتلقى أيضًا هذا التحذير ، ويشير تتبع المكدس إلى الخطاف setScriptLoaded (انظر أدناه لمعرفة الخطاف المخصص الخاص بي). لذا يبدو أنه على الرغم من أنني أستخدم useEffect ، إلا أن React ستظل تلقي تحذيرًا إذا كان المعالج setState متداخلًا في عمليات الاسترجاعات غير المتزامنة الأخرى (في حالتي ، يتم تضمينها في setTimeout و load معالج الحدث)؟ المرة الأولى التي أستخدم فيها الخطافات ، لذلك سأكون ممتنًا لأي نصيحة. شكرا لك!

/**
 * Detect when 3rd party script is ready to use
 * 
 * <strong i="11">@param</strong> {function} verifyScriptLoaded Callback to verify if script loaded correctly
 * <strong i="12">@param</strong> {string} scriptTagId 
 */

export const useScriptLoadStatus = (verifyScriptLoaded, scriptTagId) => {
    let initLoadStatus = true; // HTML already includes script tag when rendered server-side
    if (__BROWSER__) {
        initLoadStatus = typeof verifyScriptLoaded === 'function' ? verifyScriptLoaded() : false;
    }
    const [isScriptLoaded, setScriptLoaded] = useState(initLoadStatus); 

    useEffect(() => {
        if (!isScriptLoaded) {
            // need to wrap in setTimeout because Helmet appends the script tags async-ly after component mounts (https://github.com/nfl/react-helmet/issues/146)
            setTimeout(() => {
                let newScriptTag = document.querySelector(`#${scriptTagId}`);
                if (newScriptTag && typeof verifyScriptLoaded === 'function') {
                    newScriptTag.addEventListener('load', () => { 
                        return verifyScriptLoaded() ? setScriptLoaded(true) : null;
                    });
                    // double check if script is already loaded before the event listener is added
                    return verifyScriptLoaded() ? setScriptLoaded(true) : null;
                }
            }, 100);
        }
    });

    return isScriptLoaded;
};

تضمين التغريدة

هل يمكن أن يكون ذلك بسبب حقيقة أن كلاً من UNSAFE_componentWillReceiveProps و componentDidUpdate كانا في المكون في ذلك الوقت (تم ترك عنصر غير آمن هناك عن طريق الخطأ)؟

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

suhanw يرجى تقديم مثال كامل في CodeSandbox. لا أرى أي مشكلة في التعليمات البرمجية الخاصة بك والتي من المفترض أن تسبب هذا التحذير.

LabhanshAgrawal هل يمكنك نشر مجموع رقائقك الكامل من فضلك؟ أعتقد أن ما قد يحدث هو أن UNSAFE_componentWillReceiveProps (وهو ما يعادل التقديم) يستدعي setState على مكون آخر.

backend.js:6 Warning: Cannot update a component from inside the function body of a different component.
    in Term                           (created by _exposeDecorated(Term))
    in _exposeDecorated(Term)         (created by DecoratedComponent)
    in DecoratedComponent             (created by TermGroup_)
    in TermGroup_                     (created by ConnectFunction)
    in ConnectFunction                (created by Connect(TermGroup_))
    in Connect(TermGroup_)            (created by _exposeDecorated(TermGroup))
    in _exposeDecorated(TermGroup)    (created by DecoratedComponent)
    in DecoratedComponent             (created by Terms)
    in div                            (created by Terms)
    in div                            (created by Terms)
    in Terms                          (created by _exposeDecorated(Terms))
    in _exposeDecorated(Terms)        (created by DecoratedComponent)
    in DecoratedComponent             (created by ConnectFunction)
    in ConnectFunction                (created by Connect(DecoratedComponent))
    in Connect(DecoratedComponent)    (created by Hyper)
    in div                            (created by Hyper)
    in div                            (created by Hyper)
    in Hyper                          (created by _exposeDecorated(Hyper))
    in _exposeDecorated(Hyper)        (created by DecoratedComponent)
    in DecoratedComponent             (created by ConnectFunction)
    in ConnectFunction                (created by Connect(DecoratedComponent))
    in Connect(DecoratedComponent)
    in Provider
r                                     @ backend.js:6
printWarning                          @ react-dom.development.js:88
error                                 @ react-dom.development.js:60
warnAboutRenderPhaseUpdatesInDEV      @ react-dom.development.js:23260
scheduleUpdateOnFiber                 @ react-dom.development.js:21196
dispatchAction                        @ react-dom.development.js:15682
checkForUpdates                       @ connectAdvanced.js:88
handleChangeWrapper                   @ Subscription.js:97
(anonymous)                           @ Subscription.js:23
batchedUpdates$1                      @ react-dom.development.js:21887
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
checkForUpdates                       @ connectAdvanced.js:77
handleChangeWrapper                   @ Subscription.js:97
(anonymous)                           @ Subscription.js:23
batchedUpdates$1                      @ react-dom.development.js:21887
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
handleChangeWrapper                   @ Subscription.js:97
dispatch                              @ redux.js:222
e                                     @ VM64:1
(anonymous)                           @ effects.ts:11
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
(anonymous)                           @ sessions.ts:124
(anonymous)                           @ index.js:8
dispatch                              @ VM64:1
onResize                              @ terms.ts:62
(anonymous)                           @ term.js:179
e.fire                                @ xterm.js:1
t.resize                              @ xterm.js:1
e.resize                              @ xterm.js:1
e.fit                                 @ xterm-addon-fit.js:1
fitResize                             @ term.js:291
UNSAFE_componentWillReceiveProps      @ term.js:408
callComponentWillReceiveProps         @ react-dom.development.js:12998
updateClassInstance                   @ react-dom.development.js:13200
updateClassComponent                  @ react-dom.development.js:17131
beginWork                             @ react-dom.development.js:18653
beginWork$1                           @ react-dom.development.js:23210
performUnitOfWork                     @ react-dom.development.js:22185
workLoopSync                          @ react-dom.development.js:22161
performSyncWorkOnRoot                 @ react-dom.development.js:21787
(anonymous)                           @ react-dom.development.js:11111
unstable_runWithPriority              @ scheduler.development.js:653
runWithPriority$1                     @ react-dom.development.js:11061
flushSyncCallbackQueueImpl            @ react-dom.development.js:11106
flushSyncCallbackQueue                @ react-dom.development.js:11094
batchedUpdates$1                      @ react-dom.development.js:21893
notify                                @ Subscription.js:19
notifyNestedSubs                      @ Subscription.js:92
handleChangeWrapper                   @ Subscription.js:97
dispatch                              @ redux.js:222
e                                     @ VM64:1
(anonymous)                           @ effects.ts:11
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
effect                                @ ui.ts:60
(anonymous)                           @ effects.ts:13
(anonymous)                           @ write-middleware.ts:14
(anonymous)                           @ index.js:11
(anonymous)                           @ plugins.ts:538
(anonymous)                           @ plugins.ts:540
(anonymous)                           @ index.js:11
dispatch                              @ redux.js:638
(anonymous)                           @ ui.ts:54
(anonymous)                           @ index.js:8
dispatch                              @ VM64:1
(anonymous)                           @ index.tsx:162
emit                                  @ events.js:210
(anonymous)                           @ rpc.ts:31
emit                                  @ events.js:210
onMessage                             @ init.ts:50

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

السطر الذي يشير إلى الخطاف المخصص الخاص بي هو هذا: https://gist.github.com/suhanw/bcc2688bba131df8301dae073977654f#file -stack-trace-L144

سيكون رائعًا إذا كان بإمكانك إلقاء نظرة وإخباري إذا كان هناك أي شيء في تتبع المكدس قبل / بعد الخطاف المخصص الذي يجب أن أقوم بالتحقيق فيه. شكرا!

LabhanshAgrawal في المكدس الخاص بك ، يستدعي UNSAFE_componentWillReceiveProps بعض fitResize والذي يرسل إجراء Redux ، والذي بدوره يقوم بتحديث مجموعة من المكونات. ومن هنا جاءت المشكلة. حسنًا ، تغيير ذلك إلى componentDidUpdate يعمل.

suhanw في المكدس الخاص بك ، يظهر شيء يسمى ModuleImpressionTracker لإرسال إجراء Redux أثناء المنشئ. لا ينبغي أن يكون للمنشآت آثار جانبية. أعتقد أن هذا هو سبب المشكلة ، وليس سبب المشكلة.

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

تضمين التغريدة

هل يمكنك أن توضح قليلاً أنها مشكلة تتعلق بالمكونات الوظيفية والخطافات التي تقوم بها setState ، ولا يمكنك الحصول عليها بوضوح من المناقشة أعلاه.

بصراحة ، لست متأكدًا من نفسي بسبب كل عناصر إعادة التشغيل في المنتصف. إنه أمر محير للغاية بسبب كيفية تنفيذ React Redux. سيكون من الجيد أن يحصل شخص ما على صيغة واضحة تتضمن React وحدها ولكنها تستخدم مكونات صنفية.

لا يزال يبدو أن هناك الكثير من آثار المكدس التي يتم لصقها ، وليس هناك العديد من عمليات إعادة النسخ الفعلية للمشكلة الفعلية بغض النظر عن النوع.

أعتقد أن السعر urql مخصص gaearon فما يحدث هو:

  • لقد تم تثبيت <Todos /> ، والذي يقوم بجلب البيانات وعرضها
  • الإعداد السابق دفق متصل بـ urqlClient
  • نقوم بتقديم <Todos /> وهذا سينتج نفس تركيبة الاستعلام + المتغيرات لذا سيتم تحديث النتيجة لـ <Todos /> من الخطوة الأولى.
  • use-subscription لكليهما.

Repro - اضغط على "فتح" في الجزء العلوي عندما يتم عرض الاستعلام الأول.

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

تحرير: ربما وجدنا حلاً لحالة urql من خلال عدم حث جميع المشتركين على التحديث أثناء getCurrentValue من use-subscription

https://github.com/FormidableLabs/urql/commit/3a597dd92587ef852c18139e9781e853f763e930

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

JoviDeCroock هذا المثال ليس مفيدًا للغاية لأنه ليس لدي أي فكرة عما يفعله urql . :-) إذا كنت تريد الحصول على تعليقات على نمط ما ، فهل يمكنك إعداد وضع حماية يوضح هذا النمط بشكل منفصل؟

JoviDeCroock نعم ، لا يُقصد من getCurrentValue بالتأكيد أن يكون له آثار جانبية. كنا نظن أن هذا الاسم واضح جدًا بشأن ذلك.

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

function useCustomHook() {
   const dispatch = useDispatch()
   const value = useSomeOtherHook()
   dispatch(action(value))
}

لقد أصلحت هذا عن طريق تغليف الإرسالية بـ useEffect .

Glinkis هذا يبدو وكأنه نمط سيء بغض النظر. لماذا تريد إخطار الشجرة بأكملها كلما تم عرض أحد المكونات؟

gaearon نعم ، المشكلة التي كنا نحاول حلها يتم شرحها بشكل أفضل هنا ويبدو أنها شائعة جدًا.

Glinkis هذا يبدو وكأنه نمط سيء بغض النظر. لماذا تريد إخطار الشجرة بأكملها كلما تم عرض أحد المكونات؟

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

أعلم أن هذا قد يكون دون المستوى الأمثل ، لكنني لم أجد طريقة أخرى للوصول إلى بيانات التوجيه داخل محدداتي.

Glinkis من أين تأتي بيانات المسار؟

JoviDeCroock أعتقد أن أحدث توصياتنا لهذا النمط هي تصريح مخصص مجدول لجمع القمامة.

Glinkis من أين تأتي بيانات المسار؟

لست متأكدًا مما إذا كان هذا ينتمي إلي هذه المناقشة ، لكن هذا هو خطافي.

export function useOpenFolder() {
  const dispatch = useDispatch()
  const match = useRouteMatch('/:workspace/(super|settings)?/:type?/:id?/(item)?/:item?')
  const { id, item } = match?.params || {}

  useEffect(() => {
    dispatch(
      openFolder({
        id: id || '',
        item: item || '',
      })
    )
  }, [dispatch, item, id])
}

أستخدم هذه الحالة لاحقًا لمحددات مثل:

export const getActiveItem = createSelector(
  [getActiveFolderItem, getItems],
  (activeItem, items) => items.all[activeItem]
)

Glinkis نعم ، ربما دعنا useRouteMatch في المكون الرئيسي ، وتمرير المعرّف كعنصر أساسي ، ثم قراءة الخاصيات في المُحدِّد كما تفعل عادةً. (لا أعرف في الواقع كيف يتم ذلك في Redux هذه الأيام ولكن يجب أن يكون هناك طريقة ما.)

أرى ، لذلك استبعد من هذا أن UNSAFE_componentWillReceiveProps يُحسب على أنه عرض لكن componentDidUpdate ليس كذلك.

تضمين التغريدة بعض شرح الخلفية هنا :

من الناحية المفاهيمية ، تعمل React على مرحلتين:

  • تحدد مرحلة تقديم ما هي التغييرات التي يجب القيام بها لمثل DOM. خلال هذه المرحلة ، تستدعي React render ثم تقارن النتيجة بالتصيير السابق.
  • مرحلة الالتزام هي عندما يطبق React أي تغييرات. (في حالة React DOM ، يحدث هذا عندما تُدرج React عُقد DOM وتُحدّثها وتزيلها). تستدعي React أيضًا دورات الحياة مثل componentDidMount و componentDidUpdate خلال هذه المرحلة.

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

تتضمن دورات حياة طور العرض طرق مكونات الفئة التالية:

  • constructor
  • componentWillMount (أو UNSAFE_componentWillMount )
  • componentWillReceiveProps (أو UNSAFE_componentWillReceiveProps )
  • componentWillUpdate (أو UNSAFE_componentWillUpdate )
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • وظائف التحديث setState (الوسيطة الأولى)

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

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

أعتقد أنه حتى لو كان أكثر من المقصود ، في رأيي أنه من الجيد أن يكون لديك لأنه يساعد في تحسين مشاريعنا باستخدام الفصول الدراسية.

رسالة الخطأ التي تشرح نفسها بنفسها "لا يمكن تحديث مكون من داخل جسم الوظيفة لمكون مختلف"

هذا يعني التنفيذ كوظيفة بدلاً من استدعاء المكون.

مثال مثل هذا:

const App = () => {  
  const fetchRecords = () => { 
    return <div>Loading..</div>;
  };  
  return fetchRecords() // and not like <FetchRecords /> unless it is functional component.
};
export default App;

rpateld لا أعتقد أن المثال الذي تعرضه مرتبط بهذا التحذير.

https://github.com/facebook/react/pull/18330 سيحل القضايا التي لم نعتزم بدء إطلاقها.

أواجه هذه المشكلة أيضًا مع react@experimental + react-redux + redux .
image

يبدو الرمز كما يلي:

import React, { Suspense } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { userActions, cabinetActions, tokensActions } from '../../actions'
import { CircularProgress } from '@material-ui/core'
import { api } from './api'

const Cabinet = ({ resource }) => {
    resource.read()
    return <h1>cabinet</h1>
}

Cabinet.propTypes = {
    resource: PropTypes.shape({
        read: PropTypes.func
    })
}

const CabinetPage = ({
    failedToLoad,
    tokensRefreshFailed,
    logout,
    loadCabinet,
    clearTokens,
    clearCabinet
}) => {
    if (tokensRefreshFailed || failedToLoad) {
        clearTokens()
        clearCabinet()
        logout()
        return <Redirect to='/login' />
    }

    return (
        <Suspense fallback={<CircularProgress />}>
            <Cabinet resource={loadCabinet()} />
        </Suspense>
    )
}

CabinetPage.propTypes = {
    loadCabinet: PropTypes.func,
    failedToLoad: PropTypes.bool,
    tokensRefreshFailed: PropTypes.bool,
    logout: PropTypes.func,
    clearTokens: PropTypes.func,
    clearCabinet: PropTypes.func
}

const mapStateToProps = ({
    alert,
    tokens: { tokensRefreshFailed },
    cabinet: { failedToLoad }
}) => ({
    alert,
    tokensRefreshFailed,
    failedToLoad
})

const mapDispatchToProps = dispatch =>
    bindActionCreators(
        {
            cabinetLoad: cabinetActions.load,
            logout: userActions.logoutWithoutRedirect,
            loadCabinet: api.loadCabinet,
            clearCabinet: cabinetActions.clear,
            clearTokens: tokensActions.clear
        },
        dispatch
    )

export default connect(mapStateToProps, mapDispatchToProps)(CabinetPage)

يرسل loadCabinet() إجراءً غير متزامن للعرض ثلاثي الطور كما تقول المستندات المتزامنة ، ويعيد كائنًا بخاصية read() .
ومع ذلك ، لا يمكنني رؤية أي تحديثات للوالدين هنا.

@ h0tw4t3r أنت ترسل إجراء Redux أثناء

فيما يتعلق بالوضع المتزامن ، يرجى ملاحظة أن Redux غير متوافق حاليًا معه بشكل عام. لذلك قد ترغب في تجنبه إذا كنت تجرب CM.

تحديث صغير في هذا الموضوع

سنقوم بإصدار تصحيح من React قريبًا لإصلاح الإفراط في إطلاق هذا التحذير للفئات. لذلك إذا واجهت هذا ، ففكر في الانتظار بضعة أيام ثم تجربة الإصدار 16.13.1 عندما ينتهي.

إذا كنت تريد الاستمرار في البحث عن الأسباب ، آمل أن يشرح https://github.com/facebook/react/issues/18178#issuecomment -595846312 كيف.

أجد أنه من الغريب جدًا أن نفس المنطق عند استخدامه في مكون فئة لا يعطي أي تحذيرات بينما (الخطافات) الوظيفية:

المكون الوظيفي (خطاف):

import React, { Component } from "react"
import SortableTree from "react-sortable-tree"
import "react-sortable-tree/style.css"

const data = [
  {
    title: "Windows 10",
    subtitle: "running",
    children: [
      {
        title: "Ubuntu 12",
        subtitle: "halted",
        children: [
          {
            title: "Debian",
            subtitle: "gone"
          }
        ]
      },
      {
        title: "Centos 8",
        subtitle: "hardening"
      },
      {
        title: "Suse",
        subtitle: "license"
      }
    ]
  }
]

const nodeInfo = row => console.log(row)

export default class App extends Component {
  constructor(props) {
    super(props)

    this.state = {
      searchString: "",
      searchFocusIndex: 0,
      searchFoundCount: null,
      treeData: data
    }
  }

  render() {
    const { searchString, searchFocusIndex, searchFoundCount } = this.state

    const customSearchMethod = ({ node, searchQuery }) =>
      searchQuery &&
      ((node.title &&
        node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
        (node.subtitle &&
          node.subtitle.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1))

    const selectPrevMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFoundCount + searchFocusIndex - 1) % searchFoundCount
            : searchFoundCount - 1
      })

    const selectNextMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFocusIndex + 1) % searchFoundCount
            : 0
      })

    return (
      <div>
        <h2>Find the needle!</h2>
        <form
          style={{ display: "inline-block" }}
          onSubmit={event => {
            event.preventDefault()
          }}
        >
          <input
            id="find-box"
            type="text"
            placeholder="Search..."
            style={{ fontSize: "1rem" }}
            value={searchString}
            onChange={event =>
              this.setState({ searchString: event.target.value })
            }
          />
          &nbsp;
          <button
            type="button"
            disabled={!searchFoundCount}
            onClick={selectPrevMatch}
          >
            &lt;
          </button>
          &nbsp;
          <button
            type="submit"
            disabled={!searchFoundCount}
            onClick={selectNextMatch}
          >
            &gt;
          </button>
          &nbsp;
          <span>
            &nbsp;
            {searchFoundCount > 0 ? searchFocusIndex + 1 : 0}
            &nbsp;/&nbsp;
            {searchFoundCount || 0}
          </span>
        </form>

        <div style={{ height: 300 }}>
          <SortableTree
            treeData={this.state.treeData}
            onChange={treeData => this.setState({ treeData })}
            searchMethod={customSearchMethod}
            searchQuery={searchString}
            searchFocusOffset={searchFocusIndex}
            searchFinishCallback={matches =>
              this.setState({
                searchFoundCount: matches.length,
                searchFocusIndex:
                  matches.length > 0 ? searchFocusIndex % matches.length : 0
              })
            }
            generateNodeProps={row => {
              return {
                title: row.node.title,
                subtitle: (
                  <div style={{ lineHeight: "2em" }}>{row.node.subtitle}</div>
                ),
                buttons: [
                  <button
                    type="button"
                    className="btn btn-outline-success"
                    style={{
                      verticalAlign: "middle"
                    }}
                    onClick={() => nodeInfo(row)}
                  >
                    ℹ
                  </button>
                ]
              }
            }}
          />
        </div>
      </div>
    )
  }
}

image

مكون الفئة:

import React from "react";
import SortableTree from "react-sortable-tree";
import "react-sortable-tree/style.css";

const data = [
  {
    title: "Windows 10",
    subtitle: "running",
    children: [
      {
        title: "Ubuntu 12",
        subtitle: "halted",
        children: [
          {
            title: "Debian",
            subtitle: "gone"
          }
        ]
      },
      {
        title: "Centos 8",
        subtitle: "hardening"
      },
      {
        title: "Suse",
        subtitle: "license"
      }
    ]
  }
];

const nodeInfo = row => console.log(row);

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      searchString: "",
      searchFocusIndex: 0,
      searchFoundCount: null,
      treeData: data
    };
  }

  render() {
    const { searchString, searchFocusIndex, searchFoundCount } = this.state;

    const customSearchMethod = ({ node, searchQuery }) =>
      searchQuery &&
      ((node.title &&
        node.title.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1) ||
        (node.subtitle &&
          node.subtitle.toLowerCase().indexOf(searchQuery.toLowerCase()) > -1));

    const selectPrevMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFoundCount + searchFocusIndex - 1) % searchFoundCount
            : searchFoundCount - 1
      });

    const selectNextMatch = () =>
      this.setState({
        searchFocusIndex:
          searchFocusIndex !== null
            ? (searchFocusIndex + 1) % searchFoundCount
            : 0
      });

    return (
      <div>
        <h2>Find the needle!</h2>
        <form
          style={{ display: "inline-block" }}
          onSubmit={event => {
            event.preventDefault();
          }}
        >
          <input
            id="find-box"
            type="text"
            placeholder="Search..."
            style={{ fontSize: "1rem" }}
            value={searchString}
            onChange={event =>
              this.setState({ searchString: event.target.value })
            }
          />
          &nbsp;
          <button
            type="button"
            disabled={!searchFoundCount}
            onClick={selectPrevMatch}
          >
            &lt;
          </button>
          &nbsp;
          <button
            type="submit"
            disabled={!searchFoundCount}
            onClick={selectNextMatch}
          >
            &gt;
          </button>
          &nbsp;
          <span>
            &nbsp;
            {searchFoundCount > 0 ? searchFocusIndex + 1 : 0}
            &nbsp;/&nbsp;
            {searchFoundCount || 0}
          </span>
        </form>

        <div style={{ height: 300 }}>
          <SortableTree
            treeData={this.state.treeData}
            onChange={treeData => this.setState({ treeData })}
            searchMethod={customSearchMethod}
            searchQuery={searchString}
            searchFocusOffset={searchFocusIndex}
            searchFinishCallback={matches =>
              this.setState({
                searchFoundCount: matches.length,
                searchFocusIndex:
                  matches.length > 0 ? searchFocusIndex % matches.length : 0
              })
            }
            generateNodeProps={row => {
              return {
                title: row.node.title,
                subtitle: (
                  <div style={{ lineHeight: "2em" }}>{row.node.subtitle}</div>
                ),
                buttons: [
                  <button
                    type="button"
                    className="btn btn-outline-success"
                    style={{
                      verticalAlign: "middle"
                    }}
                    onClick={() => nodeInfo(row)}
                  >
                    ℹ
                  </button>
                ]
              };
            }}
          />
        </div>
      </div>
    );
  }
}

radulle هذا ليس مثالًا مفيدًا جدًا في حد ذاته. حاولت وضعه في CodeSandbox لكنه لا يعمل: https://codesandbox.io/s/clever-taussig-9xixs. هل يمكنك تحضير مثال يمكننا تجربته؟

gaearon أردت إنشاء codeandbox ولكن أحدث إصدار من المكتبة به بعض المشكلات. لم يتم طرح خطأ في الإصدارات القديمة. في الوقت الحالي ، يبدو أن الطريقة الوحيدة لإعادة إنتاجها هي تدويرها في Create React App محليًا :(

radulle ما هو الإصدار الذي يمكنني تجربته والذي سيعمل على CodeSandbox؟

يعمل gaearon 2.6.2 ويرمي الخطأ / التحذير مع هذا الإعداد:
image
لذلك بالنسبة لنفس الإعداد:
المكون الوظيفي: الأخطاء / التحذيرات
مكون الفئة: لا أخطاء / تحذيرات
ربما فاتني شيء وهم ليسا متكافئين.

نعم ، هذه إحدى الحالات التي أشرت إليها في https://github.com/facebook/react/issues/18178#issuecomment -600369392. سنقوم بكتم صوت التحذير في هذه الحالة. التحذير بحد ذاته شرعي وكما قلت بحق ، فهو يمثل مشكلة في الفصول الدراسية أيضًا من الناحية المفاهيمية. ومع ذلك ، فإن التناقض ليس منطقيًا ، لذلك سنقوم بكتمه في كلتا الحالتين في الوقت الحالي عندما يأتي من فصل دراسي (وهو كذلك ، في هذا المثال).

أعتقد أن هناك حالة استخدام مشروعة لإطلاق تحديثات الحالة من وظيفة العرض وليس من تأثير ، وذلك للحفاظ على أمر التنفيذ.

للتوضيح بمثال عملي: في تطبيقنا ، لدينا نظام خاص لإدارة فتات الخبز.

  • لدينا سياق عالمي يحمل كل فتات الخبز
  • لدينا خطاف يسمح لنا بإضافة فتات الخبز إلى هذا السياق ، مثل هذا:

    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb>{item.name}</Breadcrumb>, [item.name]);
    
  • لدينا مكون يقرأ المعلومات من السياق ، ويعرض جميع فتات التنقل. لذلك ، عندما نريد عرض فتات الخبز ، علينا ببساطة تسميته بدون معلمات.

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

لذلك ، بالاقتران مع react-router ، يمكننا فعل شيء مثل هذا:

// Main/index.tsx
import { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import Home from './Home';
import Movies from './Movies';

const Main = () => {
    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb to="/">Home</Breadcrumb>, []);

    return <Switch>
        <Route path="/movies">
            <Movies />
        </Route>
        <Route path="/" />
            <Home />
        </Route>
    </Switch>
}

// Movies/index.tsx
import { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import Detail from './Detail';
import Master from './Master';

const Movies = ({ url }) => {
    const addBreadcrumb = useAddBreadcrumb();
    addBreadcrumb(<Breadcrumb to={url}>Movies</Breadcrumb>, [url]);

    return <Switch>
        <Route path="/:id">
            <Detail />
        </Route>
        <Route path="/" />
            <Master />
        </Route>
    </Switch>
}

// Movies/Detail/index.tsx
import Breadcrumbs, { useAddBreadcrumb } from 'components/Breadcrumbs';
import React from 'react';
import { useRouteMatch } from 'react-router-dom';

const MovieDetail = ({ url }) => {
    const addBreadcrumb = useAddBreadcrumb();
    const { params: { id } } = useRouteMatch<{ id: string; }>();
    const movie = useMovie(id);

    addBreadcrumb(
        <Breadcrumb to={url}>{movie?.name}</Breadcrumb>,
        [movie?.name, url]
    );

    return <div>
        <Breadcrumbs />
    </div>
}

الآن ، إذا انتقلنا إلى /movies/gone-with-the-wind ، فستبدو فتات الخبز الخاصة بنا كما يلي:

Home > Movies > Gone with the wind

الآن ، هذه وجهة نظري: لكي يعمل ذلك ، نحتاج إلى ضمان أمر التنفيذ. في هذه الحالة ، يكون أمر التنفيذ واضحًا: أولًا Main عرضه ، ثم يتم عرض عناصره الفرعية ، والتي تتضمن Movies ، وأخيرًا MovieDetail . في هذه الحالة ، سيتم تنفيذ استدعاء addBreadcrumb بالترتيب الصحيح.

الآن ، يوضح سجل التغيير هذا:

في الحالة النادرة التي تريد فيها عمدًا تغيير حالة مكون آخر كنتيجة للتصيير ، يمكنك لف استدعاء setState في useEffect.

هذه ، في الواقع ، واحدة من الحالات النادرة التي نريد فيها عمدًا تغيير حالة مكون آخر. ومع ذلك ، إذا فعلنا ذلك ، يقترح سجل التغيير ولف addBreadcrumb (والذي في النهاية setState ) إلى useEffect ، فإن أمر التنفيذ لم يعد مضمونًا. سيتم تنفيذ الثلاثة setStates بعد انتهاء العرض ، مما يؤدي إلى حالة سباق ، وكسر نظامنا.

لا أعرف ما إذا كان هذا النظام الغريب يعتبر مضادًا للنمط أم لا ، لكن بالنسبة لي هذا منطقي ، ولم أجد أي بدائل أبسط.

لذا ، في الختام ، أرحب بهذا التحذير الجديد ، لكنني أعتقد أن الحل الأمثل بالنسبة لنا هو أن يكون لدينا طريقة لقمعه. ربما ستؤدي المعلمة الاختيارية الثانية لـ setState إلى حل المشكلة.

تضمين التغريدة

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

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

gaearon ، شكرا على البصيرة الخاصة بك. لدي سؤال واحد: هل ترتيب أول تصيير مضمون؟ لأن هذا كل ما نحتاجه للتشغيل بشكل صحيح (بمجرد أن نعرف ترتيب فتات الخبز ، لا نهتم بترتيب العروض اللاحقة).

يبدو منطقيًا بالنسبة لي أن ترتيب العرض الأول مضمون. كيف يعرف React أن المكون الرئيسي له أي توابع بخلاف ذلك؟

حول أداء عمليات العرض المتتالية ، أنت على حق تمامًا. سأبحث عن طرق لتحسين نظامنا.

تضمين التغريدة

لدي سؤال واحد: هل ترتيب أول تصيير مضمون؟ لأن هذا كل ما نحتاجه للتشغيل بشكل صحيح (بمجرد أن نعرف ترتيب فتات الخبز ، لا نهتم بترتيب العروض اللاحقة).

ليس بقوة. أعتقد أنه يحدث في الغالب للعمل في الإصدار الحالي من React ، لكن هذا قد يتغير في المستقبل. حتى اليوم ، مع ميزات مثل lazy و Suspense ، فهي ليست مضمونة.

كيف يعرف React أن المكون الرئيسي له أي توابع بخلاف ذلك؟

ترتيب الأخوة غير مضمون. لأمر الوالد / الطفل، وكنت الآباء الصحيح أن تجعل أولا؛ ومع ذلك ، قد تحتاج React إلى إعادة تصيير الأصل مرة أخرى قبل أن تصل إلى الطفل ، أو حتى بعد أن تقدم الطفل الأول ولكن قبل الطفل الثاني. مرة أخرى ، بمجرد إضافة ميزات مثل تقسيم الشفرة ، تفقد الضمانات بشكل أسرع.

هذا هش.

gaearon ، شكرا مرة أخرى. هذا هو موضع تقدير كبير جدا.

ربما يجب أن تكون هناك قاعدة ESLint تحذر من استدعاء متحولات useState داخل جسم تصيير؟

داعيا setState المكون الخاص بك خلال تقديم هو نمط المدعومة (على الرغم من أنه ينبغي أن تستخدم لماما). انها setState على مكونات أخرى سيئة. لا يمكنك الكشف عن هؤلاء بشكل ثابت.

يمكن نظريًا أن يتم ذلك باستخدام ts-eslint ، بافتراض أنهم يستخدمون أنواع React المنبع الصحيحة ، ولكن نعم ، ربما يكون هناك جهد أكبر مما يستحق.

لا أعتقد أنه يمكن القيام به دون نوع من تتبع التأثير. بمجرد أن يكون لديك وظيفة واحدة في المنتصف ، تفقد المعلومات.

أواجه هذه المشكلة أيضًا مع react@experimental + react-redux + redux .
image

يبدو الرمز كما يلي:

import React, { Suspense } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { userActions, cabinetActions, tokensActions } from '../../actions'
import { CircularProgress } from '@material-ui/core'
import { api } from './api'

const Cabinet = ({ resource }) => {
  resource.read()
  return <h1>cabinet</h1>
}

Cabinet.propTypes = {
  resource: PropTypes.shape({
      read: PropTypes.func
  })
}

const CabinetPage = ({
  failedToLoad,
  tokensRefreshFailed,
  logout,
  loadCabinet,
  clearTokens,
  clearCabinet
}) => {
  if (tokensRefreshFailed || failedToLoad) {
      clearTokens()
      clearCabinet()
      logout()
      return <Redirect to='/login' />
  }

  return (
      <Suspense fallback={<CircularProgress />}>
          <Cabinet resource={loadCabinet()} />
      </Suspense>
  )
}

CabinetPage.propTypes = {
  loadCabinet: PropTypes.func,
  failedToLoad: PropTypes.bool,
  tokensRefreshFailed: PropTypes.bool,
  logout: PropTypes.func,
  clearTokens: PropTypes.func,
  clearCabinet: PropTypes.func
}

const mapStateToProps = ({
  alert,
  tokens: { tokensRefreshFailed },
  cabinet: { failedToLoad }
}) => ({
  alert,
  tokensRefreshFailed,
  failedToLoad
})

const mapDispatchToProps = dispatch =>
  bindActionCreators(
      {
          cabinetLoad: cabinetActions.load,
          logout: userActions.logoutWithoutRedirect,
          loadCabinet: api.loadCabinet,
          clearCabinet: cabinetActions.clear,
          clearTokens: tokensActions.clear
      },
      dispatch
  )

export default connect(mapStateToProps, mapDispatchToProps)(CabinetPage)

يرسل loadCabinet() إجراءً غير متزامن للعرض ثلاثي الطور كما تقول المستندات المتزامنة ، ويعيد كائنًا بخاصية read() .
ومع ذلك ، لا يمكنني رؤية أي تحديثات للوالدين هنا.

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

const CabinetPage = ({
    alert,
    failedToLoad,
    tokensRefreshFailed,
    logout,
    loadCabinet,
    clearTokens,
    clearCabinet,
    clearAlert
}) => (
    <Suspense fallback={<MUIBackdropProgress />}>
        {alert.message && (failedToLoad || tokensRefreshFailed) ? (
            <MUIAlertDialog
                title={alert.message}
                text={errorText}
                onClose={() => {
                    clearAlert()
                    clearCabinet()
                    clearTokens()
                    logout()
                }}
            />
        ) : (
            <Cabinet resource={loadCabinet()} />
        )}
    </Suspense>
)

يعجبني هذا التحذير لأنه يجبرك على اختيار أنماط التصميم الصحيحة. الآن كل شيء يعمل بشكل لا تشوبه شائبة!

أصلحنا الحالات التي كان التحذير فيها مبالغًا فيه في 16.13.1. القضايا المتبقية شرعية وتحتاج إلى إصلاح.

ترقية gaearon للتو واختفى الخطأ! شكرا يا رفاق لعملكم!

gearon شكرا. لقد أنقذت يومي للتو :-)

على الرغم من أن الترقية لم تحل مشكلتي ، فقد أعطتني المزيد من المعلومات في وحدة التحكم للمساعدة في العثور على مشكلتي. شكرا جايرون !

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

هل يمكنني القول إن هذا بينما أفهم تمامًا المنطق الكامن وراء هذا التحذير ... يبدو الأمر تقريبًا وكأنه خيانة لما قاله فريق React للمجتمع ، لأنه يبدو أن الفريق قد علم هذه الحقائق المهمة حول كيفية البرمجة تتفاعل:

1) حافظ على حالتك عالية كما تريد في التسلسل الهرمي الخاص بك (ليس أعلى) ، ثم قم بتمرير البيانات والمحددات إلى المكونات الفرعية

2) مكونات الوظيفة رائعة! ننسى ضجيج الفصل ، قم بعمل مكونك بالكامل في وظيفة واحدة!

والآن عندما يتبع الناس هاتين القاعدتين ، ويمررون مستنبطات دولتهم إلى مكونات وظائفهم ، ويدعونهم في تلك المكونات ... تسحب البساط للخارج وتذهب "ها ، لكن لا يمكنك فعل ما قلناه لك في الواقع لكى يفعل".

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

لذا ، كل ما أطلبه حقًا هو ... أعتقد أنه سيكون من الأفضل أن يكتب أحد أعضاء الفريق شيئًا ما (مثل مقال) حيث يقول "أعلم أننا قدمنا ​​لك هاتين القاعدتين من قبل: إليك قواعد جديدة "، ثم أضف رابطًا لما تحتويه هذه المقالة في كل مكان في المستندات / ملاحظات الإصدار التي تشير إلى هذا التحذير الجديد (لذلك يمكن لكل شخص يبحث على Google" WTF هل هذا؟ "أن يتعلم الطريقة الصحيحة لترميز React ، في" new " العالمية").

machineghost : أعتقد أنك تسيء فهم ما تحذر منه الرسالة.

لا حرج في تمرير الاسترجاعات إلى الأطفال الذين يقومون بتحديث الحالة في الوالدين. كان ذلك دائمًا على ما يرام.

تكمن المشكلة في قيام أحد المكونات بوضع تحديث في قائمة انتظار في مكون آخر ، _ بينما يتم عرض المكون الأول _.

بمعنى آخر ، لا تفعل هذا:

function SomeChildComponent(props) {
    props.updateSomething();
    return <div />
}

لكن هذا جيد:

function SomeChildComponent(props) {
    // or make a callback click handler and call it in there
    return <button onClick={props.updateSomething}>Click Me</button>
}

وكما أشار دان عدة مرات ، فإن وضع تحديث في قائمة الانتظار في المكون _same_ أثناء العرض أمر جيد أيضًا:

function SomeChildComponent(props) {
  const [number, setNumber] = useState(0);

  if(props.someValue > 10 && number < 5) {
    // queue an update while rendering, equivalent to getDerivedStateFromProps
    setNumber(42);
  }

  return <div>{number}</div>
}

حسنًا ، ولكن عندما تقوم بترميز المكون الخاص بك ، فأنت لا تفكر في توقيت الأصل. هذا جزء من جمال مكونات React: التغليف.

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

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

machineghost : أنت _truly_ لا تحصل على ما أقوله هنا.

توقيت الوالدين / الطفل ليس هو المشكلة.

تحديثات حالة قائمة الانتظار _في المكونات الأخرى أثناء عرض مكون دالة _ هي المشكلة.

بحكم التعريف يجب أن يكون أحد الوالدين / الأبناء (أو الحفيد): وإلا كيف يمكن أن يكون قد تجاوز وضع الدولة؟

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

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

توقيت. لا. مشكلة.

يُسمح لمكوِّن الوظيفة بوضع تحديث حالة في قائمة الانتظار ، أثناء تقديمه ، لنفسه فقط . كما أوضح المثال الخاص بي ، يعمل هذا كمكافئ لـ getDerivedStateFromProps .

يعد وضع تحديث لأي _ أي مكون آخر في قائمة الانتظار من داخل نص العرض الفعلي لمكون دالة أمرًا غير قانوني.

هذا ما يخبرك به هذا التحذير.

لا أعرف كيف يمكنني شرح ذلك بشكل أوضح.

التوقيت ليس هو المشكلة: ليست مشكلتك وليست مشكلتي. مشكلتي هي التوثيق ، أو عدم وجودها.

لكنك قررت بدء حرب في موضوع مع شخص غريب على الإنترنت ، بدلاً من الاستماع إلى ما يقولونه و ... ليس لدي رغبة في مواصلة التواصل معك.

النقطة المهمة هي أنه لم تتغير أي قواعد. لطالما كان هذا نمطًا خاطئًا. يتم الآن تمييزه حتى تدرك أن شفرتك بها أخطاء.

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

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

مرحبًا يا رفاق ، دعنا نهدأ قليلاً. 🙂

markerikson أقدر مشاركتك ولكني أعتقد أن هذه المناقشة تزداد سخونة بعض الشيء.

machineghost شكرا للتعبير عن مخاوفك. أعلم أنه أمر مزعج عندما تظهر تحذيرات جديدة لأنماط ربما بدت غير ضارة من قبل.

أوافق على أن هذا التحذير يتطلب بعض السياق المسبق. في الأساس ، كنت بحاجة إلى معرفة شيئين من عصر الصف:

  • أنه لا يجب عليك تعيين الحالة أثناء التقديم. الفصول تحذر دائما من هذا

  • جسم مكون الوظيفة هذا هو في الأساس نفس الشيء مثل طريقة عرض مكون الفئة.

إن إغفالنا فعلاً هو أن setState على مكون آخر أثناء جسم مكون الوظيفة لم تحذر من قبل. يمكنك أن تستنتج أنه نمط سيء من النقطتين أعلاه ، لكن من العدل أن نقول إنه لا يمكن للمرء أن يدرك ذلك. نأسف للإزعاج.

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

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

كل ما كنت أحاول إيصاله هو ، حاليًا ، عندما تلقيت هذا التحذير ، فعلت ما يفعله الجميع: بحثت عنه في غوغل. ثم عثرت على صفحة تقول "لدينا هذا التحذير الجديد".

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

لكن مرة أخرى ، الخطافات رائعة ، وفريق React رائع ، وحتى هذا التحذير الجديد رائع (أنا متأكد من أنه يتغلب على الجحيم باكتشاف الخطأ الذي يحاول الاحتراز منه). إذا كان أي شخص مدينًا لأي شخص بشخصية aplogoy ، فأنا أنا لا أقود ذلك.

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

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

أتمنى أن يساعد ذلك إلى حد ما!

سأضيف سنتي إلى هذه المناقشة وأتفق مع machineghost على أنه كان هناك الكثير من الخلط منذ إدخال المكونات الوظيفية

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

لقد كنت في عربة الوظائف لفترة طويلة (في محاولة لتجنب الفصول مع Recompose وحتى قبل الخطافات) حتى أنني لا أتذكر تلك التحذيرات الصفية.

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

ربما لا يوجد أي شيء هنا ، ولكن ربما شيء مثل "إذا غيّر المكون الوظيفي A حالته ، فلا يجب أن يجعل المكون الفرعي B يمرر أداة ضبط الحالة إليه (يجب أن يعود في الحال أو شيء من هذا القبيل) ، لأنه إذا يعرض المكون الفرعي وحالة التغييرات التي ستواجهها ".

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

شكرا مرة أخرى على الرغم من!

أعتقد أننا وصلنا إلى النقطة التي تجاوز فيها عمر هذا الخيط فائدته.

الحالة والنقطة هما ComponentDidMount و Unmount ، أولاً قيل لنا استخدام مكونات الوظيفة ، ثم استخدم useEffect مع مصفوفة فارغة ، ثم قيل لنا أن هذا ليس جيدًا ، والآن لدينا رسالة الخطأ هذه.

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

وبينما أقدر ردك ، كنت أتمنى فقط "القواعد العامة" ، والمبادئ التوجيهية ، وأفضل الممارسات ، وما إلى ذلك

لطالما كانت القاعدة الأساسية هي "عدم إجراء آثار جانبية أثناء العرض". فكر في التقديم كحساب خالص. تدخل الآثار الجانبية في مكان مختلف (طرق دورة الحياة في الفئات ، أو useEffect في مكونات الوظيفة). ليس هناك ما هو أكثر من ذلك بكثير.

"إذا غيّر المكون الوظيفي A الحالة ، فلا ينبغي أن يجعل المكون الفرعي B يمرر أداة ضبط الحالة إليه (يجب أن يعود في الحال أو شيء ما) ، لأنه إذا تم عرض المكون الفرعي وتغيير الحالة ، فستواجه مشكلات".

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

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

الموضوع العام في كلتا هاتين المحادثات هو أننا نسير في دوائر. لقد تحولت المناقشة إلى meta ، وبدلاً من الحديث عن حالات محددة نناقش العموميات الغامضة. من المحتمل أننا نسيء فهم بعضنا البعض ، لكن عدم وجود أمثلة ملموسة يجعل حل سوء التفاهم هذا مستحيلاً.

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

كتحديث صغير (آسف لإجراء اختبار ping للجميع) ، سمعت https://github.com/final-form/react-final-form/issues/751#issuecomment -606212893 حل مجموعة من هذه الأخطاء للأشخاص الذين كانوا يستخدمون ذلك مكتبة.

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