Redux: سؤال: كيف تختار بين متجر Redux وحالة React؟

تم إنشاؤها على ٢٧ يناير ٢٠١٦  ·  26تعليقات  ·  مصدر: reduxjs/redux

question

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

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

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

القاعدة الأساسية هي: افعل ما هو أقل صعوبة.

ال 26 كومينتر

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

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

القاعدة الأساسية هي: افعل ما هو أقل صعوبة.

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

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

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

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

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

للتوضيح ، تعليق

حالة التطبيق هي مجال Redux وحالة UI هي مجال React.

لاحظ أنني لا أطالب بهذا. أعتقد أنه من الجيد الاحتفاظ ببعض حالة التطبيق في React وبعض حالات واجهة المستخدم في Redux. لا أعتقد أنه يجب الفصل بينهم بواسطة المجالات.

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

أولاً ، أنا بالتأكيد لم أقصد وضع الكلمات في فم gaearon ، آسف لذلك.

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

لذا اسمحوا لي أن أوضح موقفي. أوافق على أن كل شيء تقريبًا يجب أن يكون في Redux ، بما في ذلك حالة المسار (كما فعلت في تنفيذ TodoMVC مع Redux و TypeScript . لقد ذكرت حالة المسار على وجه التحديد

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

أعتقد أن هذا السؤال شخصي ومعقد حقًا ، لذلك اتخذت قرارًا صعبًا مع فريقي اليوم ، لا تهتم:

  • بالنسبة للحاوية غير القابلة لإعادة الاستخدام ، والتي لها اتصال بـ Redux ، ما عليك سوى وضع كل شيء في متجر Redux ، حتى في حالة واجهة المستخدم الصغيرة مثل حالة وجود مشروط ، لا تستخدم this.setState .
  • بالنسبة للمكوِّن القابل لإعادة الاستخدام ، والذي لا علاقة له بـ Redux ، استخدم حالة React.

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

gaearon @ lionng429xogeny أي عيوب يمكن ان يخطر لك هذا النهج؟

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

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

لقد حصلت على HOC كنقطة انطلاق في https://gist.github.com/markerikson/554cab15d83fd994dfab ، إذا كان أي شخص يهتم.

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

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

في تطبيقاتنا قمنا بحل هذه المشكلة بأسلوب يشبه الاتصال. https://github.com/Babo-Ltd/redux-state

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

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

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

gaearon هل ترغب في مشاركة تجاربك حول كيفية تحديد متى وكيف يتم اعتبارها _awkward_؟ على سبيل المثال ، هل يمكنك إعطاء بعض السيناريوهات / الأمثلة النموذجية التي تعتقد أنها محرجة بدرجة كافية إذا تم وضعها في ذرة حالة التطبيق؟ شكرا لك مقدما!

idavollen يمكنني أن أعطي زوجين:

  • حالة فتح / إغلاق منسدلة ، وإلا فسيتعين عليك تتبع جميع مكونات القائمة المنسدلة في متجرك
  • عندما تقوم بإلغاء تغيير <input> ، يمكنك استخدام الحالة لتحديث القيمة على الفور (ولا تخاطر بالمكونات الأخرى لإعادة العرض دون داع) وتحديث المتجر بطريقة مخفضة

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

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

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

أتساءل ما هو أفضل نهج لإدارة حالات مربعات الاختيار هذه في هذه الحالة؟

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

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

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

الأمر متروك لك لتقرير الحالة التي يتم إجراؤها في Redux وما الذي يظل محليًا بالنسبة للمكون. قد ترغب في قراءة http://redux.js.org/docs/faq/OrganizingState.html#organizing -state-only-redux-state و http://redux.js.org/docs/faq/Performance. html # performance -scaling لبعض المعلومات ذات الصلة.

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

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

يمكن تنفيذ هذه البنية يدويًا ، أو استخدام بعض المكتبات مثل redux-fractal و redux-ui وما إلى ذلك.

أو ، كما أشار دان ، يمكنك حتى تنفيذ نهج نمط المخفض لتحديث حالة المكون أيضًا! انظر https://twitter.com/dan_abramov/status/736310245945933824

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

@ akshay2604 : مرة أخرى ، الأمر متروك لك. انظر الروابط التي نشرتها في التعليقات السابقة في هذا الموضوع. يمكنك بالتأكيد استخدام setState أثناء اختبار المكونات ، خاصة إذا كنت تستخدم مكتبة Enzyme للمساعدة في اختبارها.

اسمحوا لي أن أشارك أفكاري.

أقوم بتصنيف الحالة إلى 3 فئات عامة وفقًا لكيفية ارتباطها بإدخال / إخراج التطبيق:

  • "ذاكرة التخزين المؤقت": حالة تمثل نسخة طبق الأصل من البيانات الدائمة. مثال:
{ "todos": [{ id: 1, title: "title" }, { id: 2, title: "title" }] }
  • "التغييرات": حالة تصف التغييرات المعلقة على البيانات الدائمة. مثال:
{ "changeTodo": { title: "title", action: "add", done: false },
{ id: 1, title: "changed title", action: "modify", done: true }, { id: 2, action: "delete" } }
  • "عرض": حالة تنقل خيارات العرض مثل التصفية الحالية ، والفرز ، وما إلى ذلك. مثال:
{ "displayOptions": { searchTerm: "title", sort: ["title", "desc"], filter: "done=false" } }

لا تخلط بين cache changes ، ولا تخلط بين cache view ، ولا تخلط view مع changes .

بالطبع تحتاج إلى دمج هذه بطريقة ما لعرضها في مكون تفاعل واحد. بحيث يصبح الكائن "المدمج" الخاص بك:

{ "todos": [{id: undefined, title: "title", done: false }, { id: 1, title: "changed title" }] }

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

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

مرحبًا ، أتعمق في React / Redux لبضعة أيام ، وأفهم القرار الذي تم اتخاذه لاختيار ما إذا كان ينبغي ترقية بعض المعلومات للتخزين أو الاحتفاظ بها في الحالة المحلية المكونة.

ولكن ماذا لو تطلب إجراء واحد كلاهما في وقت واحد؟ إليك حالة الاستخدام الشائعة جدًا:

  1. [سياق] متجر Redux عبارة عن صفحة واحدة { modelItems: myModelItems } . يخزن (جزء من / إسقاط) نموذج الخادم
  2. [سياق] يتكون العرض من مكون ذكي واحد MyItemsView يعرض قائمة واجهة المستخدم للعناصر المخزنة / المخزنة مؤقتًا. عند التركيب ، يقوم المكون بتشغيل نموذج عناصر الجلب. إدخال المكون: myModelItems . ناتج المكون: مشغل الإجراء fetchModelItems . المكونات الداخلية للمكون: state.busy و state.errorId
  3. [خطة] عندما يقوم MyItemsView بتشغيل fetchModelItems ، يتم إرسال طلب HTTP إلى الخادم لجلب النموذج وتخزينه مؤقتًا في المتجر. هذا الإجراء غير المتزامن له 4 مراحل وكثير من الخطافات: onStarted (طلب HTTP على وشك الإقلاع ، يبدأ تحميل القرص الدوار في المكون) ، onEnded (هبطت استجابة HTTP ، توقف التحميل في المكون ، لا يزال المرء لا ينظر إلى حالة الاستجابة) ، onFailed (يظهر إشعار خطأ في المكون) و onSucceed (يتم تحديث متجر Redux ثم يتم إعادة توجيه ذاكرة التخزين المؤقت للنموذج التي يحملها إلى مكون التقديم)

لذلك ، كيفية تقسيم مشغل الإجراء fetchModelItems بحيث:

  • onStarted و onEnded و onFailed لا تصل إلى متجر Redux؟ هذا لأن onStarted و onEnded حالات عابرة لواجهة مستخدم يجب ألا يكون لها تفاعل مع ذاكرة التخزين المؤقت للنموذج. الشيء نفسه ينطبق على onFailed (تظل ذاكرة التخزين المؤقت للنموذج دون تغيير وستعرض واجهة المستخدم إشعارًا بالخطأ). سيعرض MyItemsView حالة واجهة المستخدم هنا ، وليس النموذج المخزن مؤقتًا
  • onSucceed يصل إلى متجر Redux؟ هذا لأنه فاز في يانصيب ذاكرة التخزين المؤقت للنموذج ، وتم منحه حق الوصول إلى مصدر الحقيقة ، وسيستحم ويعيد السوائل ، ثم يعرض myModelItems في MyItemsView

حول التنفيذ في محاولتي الحالية ، يعتني Redux Thunk بمشغل الإجراء fetchModelItems . أشعر أنني اخترت مسار Redux هناك ، وأنني لا أستطيع تغيير رأيي بعد الآن لأقول "فقط قم بتمرير النجاح غير المتزامن إلى متجر Redux ، واحتفظ بأي شيء آخر في حالة مكون React".

في حيرة من هذا ، أجلب النجاحات للتخزين ، والتي تعيد توجيهها إلى المكون المتصل (أي حاوية Redux). ولكن لا يوجد تحميل دوّار وجهاز إرسال رسالة خطأ بائس مسجّل بوحدة التحكم. لا أريد تحديد store.ui.myItemsView.busy ولا store.ui.myItemsView.errorId ، ولكن فقط أحصل على هذه المعلومات في حالة المكون.

@ باسكالاف :
هذا هو تعقب الأخطاء ، وليس نظام دعم. بالنسبة لأسئلة الاستخدام ، يرجى استخدام Stack Overflow أو Reactiflux حيث يوجد الكثير من الأشخاص المستعدين لمساعدتك - ربما تحصل على إجابة أفضل بشكل أسرع. شكر!

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