كيفية التعامل مع الدولة في رد الفعل. التعليمات المفقودة.

التحفيز

في الآونة الأخيرة كان هناك الكثير من النقاش حول كيفية إدارة الدولة في React. يزعم البعض أن setState () لا يعمل كما تتوقع. أن لديك خارج الحالة باستخدام حاوية حالة تشبه Flux وتجنب حالة المكون بالكامل.

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

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

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

إذا كنت تتعلم React من البداية ، فقد صاغ Pete Hunt أفضل نصيحة يمكنك الحصول عليها. حتى مؤسس فريق Redux و React في فريق Dan Abramov يوصي به:

هذا يعني أنك بحاجة إلى فهم كيفية التعامل مع الحالة بطريقة React قبل التفكير في Flux.

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

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

ما أود أن أشير له هو:

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

الغرض من قسم الأسئلة الشائعة التالي هو تخفيف تعقيدات معالجة الحالة في React.

أسئلة مكررة.

كيف تعمل الدولة؟

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

React بتخزين حالة المكون في this.state. يمكنك تعيين القيمة الأولية لهذه الحالة. بطريقتين مختلفتين. كل واحد يتوافق مع طريقة إنشاء المكون:

/ / باستخدام React.createClass
var Counter = React.createClass ({
    getInitialState: function () {
        return {counter: 0}؛
    }،
    ...
})؛
// باستخدام فصول ES6
فئة عداد يمتد React.Component {
    منشئ (الدعائم) {
        السوبر (الدعائم)؛
        this.state = {counter: 0}؛
    }
    ...
}

يمكن تغيير حالة المكون عند الاتصال:

this.setState (البيانات ، رد الاتصال) ؛

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

دعنا نلقي نظرة على مثال:

ما يجب أن أبقى في حالة رد الفعل؟

أجاب دان أبراموف على هذا السؤال بتغريدة واحدة:

يقول بشكل أساسي لا تحتفظ بالولاية محسوبة من الدعائم ، ولا أي ولاية غير مستخدمة في طريقة التقديم () دعنا نمثل:

// لا تكرر البيانات من الدعائم في الولاية
// أنتيباترن
الطبقة المكون يمتد React.Component {
    منشئ (الدعائم) {
        السوبر (الدعائم)؛
        this.state = {message: props.message}؛
    }
    
    يجعل() {
        return 
{this.state.message}
؛     } }

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

// مثال أفضل
الطبقة المكون يمتد React.Component {
    يجعل() {
        return 
{this.props.message}
؛     } }

الأمر نفسه ينطبق عندما تحتفظ بالدولة بناءً على حساب الدعائم:

// لا تمسك بالدولة بناءً على حساب الدعائم
// أنتيباترن
الطبقة المكون يمتد React.Component {
    منشئ (الدعائم) {
        السوبر (الدعائم)؛
        this.state = {fullName: `$ {props.name} $ {props.lastName}`}؛
    }
    
    يجعل() {
        return 
{this.state.fullName}
؛     } }
// نهج أفضل
الطبقة المكون يمتد React.Component {
    يجعل() {
        const {name، lastName} = this.props؛
        return 
{`$ {name} $ {lastName}`}
؛     } }

على الرغم من أنه لا يوجد خطأ في تحديد الحالة الأولية استنادًا إلى الدعائم إذا أوضحت أن البيانات الأولية هي فقط:

/ / ليس مضاد
الطبقة المكون يمتد React.Component {
    منشئ (الدعائم) {
        السوبر (الدعائم)؛
        this.state = {count: props.initialCount}؛
        this.onClick = this.onClick.bind (هذا) ؛
    }
    
    يجعل() {
        return 
{this.state.count}
؛     }          عند النقر() {         this.setState ({count: this.state.count + 1}) ؛     } }

اخيرا وليس اخرا:

// لا تعقد حالة لا تستخدمها للتقديم.
// يؤدي إلى إعادة تقديم غير ضرورية وتناقضات أخرى.
// أنتيباترن
الطبقة المكون يمتد React.Component {
    منشئ (الدعائم) {
        السوبر (الدعائم)؛
        this.state = {count: 0}؛
    }
    
    يجعل() {
        return 
{this.state.count}
؛     }          componentDidMount () {         الفاصل الزمني const = setInterval (() => (             this.setState ({count: this.state.count + 1})         ) ، 1000) ؛
        this.setState ({} الفاصل)؛
    }
    componentWillUnmount () {
        clearInterval (this.state.interval)؛
    }
}
// نهج أفضل
الطبقة المكون يمتد React.Component {
    منشئ (الدعائم) {
        السوبر (الدعائم)؛
        this.state = {count: 0}؛
    }
    
    يجعل() {
        return 
{this.state.count}
؛     }          componentDidMount () {         this._interval = setInterval (() => (             this.setState ({count: this.state.count + 1})         ) ، 1000) ؛     }
    componentWillUnmount () {
        clearInterval (this._interval)؛
    }
}

هل صحيح أن setState () غير متزامن؟

الجواب القصير: نعم.

بشكل أساسي عندما تستدعي setState () React بجدولة أحد التحديثات ، يتم تأخير العمليات الحسابية حتى يتم الضرورة. وثائق React مضللة بعض الشيء حول هذا:

يبدو أن هاتين الجملتين في تناقض. دعنا نجرب قليلاً لنرى ما يحدث:

الطبقة المكون يمتد React.Component {
    منشئ (الدعائم) {
        السوبر (الدعائم)؛
        this.state = {count: 0}؛
        this.onClick = this.onClick.bind (هذا) ؛
    }
    
    يجعل() {
        return 
{this.state.count}
؛     }          عند النقر() {         this.setState ({count: this.state.count + 1}) ؛         console.log (this.state.count)؛     } }

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

ولكن ، ماذا يحدث إذا كان الحدث يأتي من مصدر خارجي؟

// استدعاء setState () مرتين في نفس سياق التنفيذ هو ممارسة // سيئة. يتم استخدامه هنا لأغراض التوضيح. بدلاً من ذلك استخدم // تحديث ذري في الكود الحقيقي
الطبقة المكون يمتد React.Component {
    منشئ (الدعائم) {
        السوبر (الدعائم)؛
        this.state = {count: 0}؛
    }
    
    يجعل() {
        return 
{this.state.count}
؛     }          componentDidMount () {         this._interval = setInterval (() => {             this.setState ({count: this.state.count + 1}) ؛             console.log (this.state.count)؛             this.setState ({count: this.state.count + 1}) ؛             console.log (this.state.count)؛         } ، 1000) ؛     }
componentWillUnmount () {
        clearInterval (this._interval)؛
    }
}

يفرقع، ينفجر! تُرجع القيمة الحالية كما تقترح الوثائق ، حتى استدعاء setState () مرتين في نفس سياق التنفيذ. ذلك لأن React لا يعرف ما يكفي لدفعة التحديث ويجب عليه تحديث الحالة في أقرب وقت ممكن.

هذا أمر صعب ، أقترح دائمًا معاملة setState () على أنه غير متزامن وستتجنب المتاعب.

سمعت أنه في ظل ظروف معينة ، فإن استدعاء setState () لا يؤدي إلى إعادة تقديم. ما هي تلك الظروف؟

  1. عندما تقوم بالاتصال بـ setState () داخل componentWillMount () أو componentWillRecieveProps () ، فلن يؤدي إلى إعادة تقديم أي ملف إضافي. الرد دفعات التحديثات.
  2. عندما تقوم بإرجاع false من shouldComponentUpdate (). بهذه الطريقة يتم تخطي الأسلوب render () بالكامل مع componentWillUpdate () و componentDidUpdate ().
ملحوظة:
إذا كنت ترغب في البحث أكثر في دورة حياة المكون ، فقد كتبت بالفعل منشورًا عليها.

الاستنتاجات

يمكن معالجة الحالة بشكل صحيح في React يكون تحديا. آمل الآن أن يكون لديك صورة أوضح.

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

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