Redux: أعطي الحالة الأولية في createStore ، ثم أظهر combeducers أحد مخفضاتي عاد غير محدد أثناء التهيئة

تم إنشاؤها على ٢٣ أغسطس ٢٠١٧  ·  27تعليقات  ·  مصدر: reduxjs/redux

في مخفضات:

const todoBlog = combineReducers({
  blogTypeVisibilityFilter,
  blogs
})

في blogTypeVisibilityFilter:

const blogTypeVisibilityFilter = (state, action)=>{
  switch (action.type) {
    case 'BLOG_TYPE_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

export default blogTypeVisibilityFilter;

في المدونات:

const blogs = (state,action)=>{
  return state
}

في createStore:

const initialState = {
  blogTypeVisibilityFilter:'SHOW_ALL_BLOG',
  blogs:data.data,
}

const store = createStore(reducer,initialState,window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

ثم يظهر خطأ:

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

ولكن عندما أتغير

const todoBlog = combineReducers({
  blogTypeVisibilityFilter,
  blogs
})

ل

const todoBlog = (state={},action)=>{
  return{
    blogTypeVisibilityFilter:blogTypeVisibilityFilter(state.blogTypeVisibilityFilter,action),
    blogs:blogs(state.blogs,action)
  }
}

في علبة التروس تعمل بشكل جيد وبدون أي أخطاء

لماذا أستخدم CombinedReducers يحدث خطأ؟

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

هذا هو تعقب الأخطاء ، وليس نظام دعم. لأسئلة الاستخدام ، يرجى استخدام Stack Overflow أو Reactiflux. شكرا!

ال 27 كومينتر

هذا هو تعقب الأخطاء ، وليس نظام دعم. لأسئلة الاستخدام ، يرجى استخدام Stack Overflow أو Reactiflux. شكرا!

لدي نفس المشكلة.

خطوات:

  1. صورة متجر بسيط شكل { foo, bar } .
  2. قم بإنشاء مخفض بسيط مثل simpleReducer = state => state .
  3. اجمع المخفضات في reducers = combineReducers({ foo: simpleReducer, bar: simpleReducer }) .
  4. أنشئ حالة بقيمة أولية بقيمة createState(reducers, { foo: Obj1, bar: Obj2 }) .
  5. الحصول على خطأ Reducer "foo" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state

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

timdorr هل يمكنك إعادة فتح القضية؟

الحل هو جعل simpleReducer = (state = null) => state . لكن هل هو جيد؟

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

"الحالة الأولية" لها معانٍ متعددة. إنها القيمة المرجعة الافتراضية للمخفض والوسيطة الثانية لـ createStore. أعتقد أن هناك مشكلة. هل يمكن أن تشرح لماذا تقوم شركة CombinationReducers بفحص مخفضات الحالة الأولية و createState لا؟

@ Vittly : combineReducers هو رأي متعمد ، بينما createStore ليس كذلك. يستدعي createStore أي وظيفة مخفض الجذر التي منحتها له ، ويحفظ النتيجة. يفترض combineReducers أنه إذا كنت تستخدمه ، فأنت تشتري مجموعة محددة من الافتراضات حول كيفية تنظيم الحالة وكيف يجب أن تتصرف المخفضات المدمجة.

قد ترغب في قراءة # 191 و # 1189 ، اللذان يدخلان في السجل والتفاصيل حول سبب رأي combineReducers وما الآراء التي لديه.

لذا فإن المشكلة هي (باختصار) "إذا قمت بدمج المخفضات ، يمكنك أيضًا تقسيم شجرة متجرك وقد يفوتك شيء ما أو تعمد تحميل جزء فقط من المتجر عمدًا في createStore 2nd arg. لجعل شجرة المتجر أكثر اتساقًا ، استخدم assertReducerShape". حسنًا ، شكرًا على الحكام

نفس المشكلة هنا.
لذلك أستخدم createStore وأمرر الحالة الأولية. لكن في ملف reduct.js ، ستتحقق الوظيفة assertReducerShape redux من مخفّضاتي عند تمرير حالة undefined .

IMHO هذا خطأ.

JoseFMP :

بالتأكيد. لقد قدمت مشكلة في مشكلة في Stackoverflow:
https://stackoverflow.com/questions/53018766/why-redux-reducer-getting-undefined-instead-of-the-initial-state

القضية واضحة ومباشرة. لذا ، إذا قمت بتعيين حالة أولية عند إنشاء المتجر ... فلماذا تريد إعادة التشغيل تقييم المخفض بحالة undefined ؟ هذا يجعل كل المخفّضين يعيدون حالة جديدة undefined .

لكن هذا هراء ، لأننا نجتاز حالة أولية.
إذن هذا فشل:

import { combineReducers, createStore } from 'redux';

const configReducer = (config: any, action: any): any =>{
        return config;
}

const customData = (customData: any, action: any): any =>  {
        return customData;
}
const reducers = combineReducers({config: configReducer, customData: customDataReducer})

const defaultConfig = "cool config";
const data = "yieaah some data";

var initialState = {config: defaultConfig, customData: data};
const store = createStore(reducers, initialState) // at this point redux calls all the reducers with 'undefined' state. Why?

JoseFMP :

آه ، أعتقد أنني أعرف ما هي المشكلة. هذا خاص بكيفية عمل combineReducers() .

يتوقع combineReducers أن جميع "مخفضات الشرائح" التي تقدمها ستتبع قاعدتين. على وجه الخصوص ، يتوقع أن _if_ تم استدعاء مخفضك بـ state === undefined ، فسيعيد قيمة افتراضية مناسبة. للتحقق من ذلك ، سيقوم combineReducers() باستدعاء المخفض الخاص بك بـ (undefined, {type : "SOME_RANDOMIZED_ACTION_TYPE"}) لمعرفة ما إذا كان سيعود بقيمة غير محددة أو قيمة "حقيقية".

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

ما عليك سوى تغيير الإعلانات إلى شيء مثل configReducer = (config = {}, action) ، إلخ ، وسيؤدي ذلك إلى إصلاح التحذيرات.

مرة أخرى ، لنكون واضحين: هذا _ ليس خطأ. هذا هو التحقق من السلوك.

@ hejmarkerikson شكرا لك على ردك.
بعض التعليقات:

1) لا ، فهي ليست خاصة بـ combineReducers . إذا لم أستخدم combineReducers وقمت بتطبيق مخفض الجذر الخاص بي ، فسيحدث نفس الشيء.

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

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

شكرا markerikson .
حول الوثائق:
image

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

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

للحصول على رمز المثال:

import { createStore } from 'redux';

const configReducer = (config: any, action: any): any =>{
        return config;
}

const customReducer = (customData: any, action: any): any =>  {
        return customData;
}

const reducers = (currentState: IAppState, action: any): IAppState => {

    var appStateToEvaluate: any;
    if (currentState) { //needs to add this to pass the `undefined` check of redux
        appStateToEvaluate = currentState;
    }
    else{
        //why redux is doing this ?!
        appStateToEvaluate = {}
    }
    const newState: IAppState = {
        cvConfig: configReducer(appStateToEvaluate.config, action),
        personalData: customReducer(appStateToEvaluate.customData, action)
    }

    return newState;
}

const defaultConfig = "cool config";
const data = "yieaah some data";

var initialState = {config: defaultConfig, customData: data};
const store = createStore(reducers, initialState) // at this point redux calls all the reducers with 'undefined' state. Why?

JoseFMP : أعتقد أن الاختلاف الرئيسي الذي تفتقده هنا هو أنه في هذا المثال ، عالجت وظيفة المخفض _ بالفعل _ الحالة التي يكون فيها state هو undefined ، باستخدام صيغة الوسيطات الافتراضية ES6:

function todoApp(state = initialState, action) {

لذا فإن النصيحة في البرنامج التعليمي صحيحة - المخفض _يجب_ دائمًا إرجاع الحالة الحالية ، بافتراض أنه تم التعامل مع الحالة غير المحددة بالفعل_.

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

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

شكرا لك على دعمك مارك.

يمكننا بالتأكيد محاولة إعادة صياغة بعض الصياغة كجزء من تجديد المستندات الأكبر لدينا (# 2590).

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

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

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

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

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

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

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

لا أتذكر combineReducers اتصل بـ assertReducerShape في الإصدارات السابقة. في الكود الخاص بي ، undefined حالة غير صالحة لمخفّضاتي. لا أحتاج إلى أجهزة تقليل الاختزال لضمان ذلك بالنسبة لي ، فأنا بحاجة إلى "دمج المخفضات" وإعادة إنشاء كائن الجذر إذا تغير شيء ما. إنها تتعدى قليلاً ما كنت أتوقعه. المنظمة البحرية الدولية ، إنها شديدة العزم الآن.

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

lukescott : هذا الشيك موجود منذ سبتمبر 2015:

https://github.com/reduxjs/redux/commit/a1485f0e30ea0ea5e023a6d0f5947bd56edff7dd

ونعم ، combineReducers() _ عن عمد _ عاقدة العزم. إذا كنت لا تريد هذه التحذيرات ، فمن السهل أن تكتب وظيفتك المماثلة بدون هذه الشيكات.

كمثال محدد ، راجع جوهر Dan's Redux في 100 سطر بدون الشيكات .

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

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

لست متأكدًا حقًا من تغيير السلوك الذي تشير إليه. هل يمكنك الإشارة إلى أمثلة محددة؟

تحدد صفحة تهيئة مستندات الحالة التفاعلات بين الوسيطة preloadedState لـ createStore ، معالجة state = initialState للمخفض ، و combineReducers() . يعتمد ذلك على إجابة Stack Overflow التي كتبها دان في أوائل عام 2016 ، ولم يتغير شيء ذو مغزى في هذا الصدد الذي أعرفه.

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

المشكلة التي لدي هي أن المخفض الخاص بي محدد على النحو التالي:

reducer(state: State, action: Action)

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

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

reducer(state: State | undefined, action: Action)

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

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

نعم. كما هو موضح في صفحة المستندات هذه ، عند استخدام combineReducers ، فإن التوقع هو تحديدًا أن كل مخفض شريحة مسؤول عن إعادة حالته الأولية ، ويجب أن يفعل ذلك ردًا على sliceReducer(undefined, action) . من النقطة combineReducers ، رمزك _is_ عربات التي تجرها الدواب لأنها لا تلتزم بالعقد المتوقع ، وبالتالي فهي تخبرك أن الرمز خاطئ.

هل أنت لا تقدم بالفعل حالة أولية؟ كيف تبدو الشفرة في الواقع؟

أقدم حالة أولية من خلال createStore. لكن هذه الحالة الأولية لم يتم تمريرها إلى مخفضي لأن assertReducerShape تستدعي صراحة المخفض الخاص بي بـ undefined ، على الرغم من إجازتي للحالة الأولية:

https://github.com/reduxjs/redux/blob/231f0b32641059caab3f98a3e04d3afaad19a7d1/src/combineReducers.js#L68

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

ما لا أفهمه هو سبب أهمية assertReducerShape. لقد تحقق بالفعل من undefined هنا في وقت التشغيل:

https://github.com/reduxjs/redux/blob/231f0b32641059caab3f98a3e04d3afaad19a7d1/src/combineReducers.js#L169

assertReducerShape يبدو نوعًا من الزائدة عن الحاجة. لكن في حالتي ، ضمانات نوع الاستراحات ... ما هو الشيء الذي تحاول تأكيده؟

إذن ، هذه حقًا مشكلة تفاعل من TypeScript ، إذن؟

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

combineReducers() بلغة JS ، وتحاول إجراء فحوصات وقت التشغيل.

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

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

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

export default function itemReducer(state = initialState.items | undefined, action){ 
    switch(action.type) 
   { 
         case "LOAD_ITEMS_SUCCESS":
               if(action.items==undefined) { 
                        action.items=[]; 
               }
                return action.items

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

هتافات!

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

القضايا ذات الصلة

CellOcean picture CellOcean  ·  3تعليقات

rui-ktei picture rui-ktei  ·  3تعليقات

parallelthought picture parallelthought  ·  3تعليقات

vraa picture vraa  ·  3تعليقات

mickeyreiss-visor picture mickeyreiss-visor  ·  3تعليقات