Dva: كيفية إلغاء التأثيرات

تم إنشاؤها على ٨ يونيو ٢٠١٨  ·  11تعليقات  ·  مصدر: dvajs/dva

رمز إعادة إظهار المشكلة: (يُرجى تقديم رمز أو خطوات قابلة لإعادة الإنتاج)

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

عندما أغادر الصفحة "أ" أو الصفحة "ب" ، أريد مسح البيانات الموجودة في النموذج المقابل حتى لا تصبح بيانات قذرة عند إعادة استخدامها في المرة القادمة.
لقد كتبت مخفضًا واضحًا في النموذج ، في طريقة componentWillUnmount
إرسال ({type: $ {model.namespace} / clear})

السلوك المتوقع: (التأثير الطبيعي المتوقع)

يقوم المستخدم بإدخال الصفحة "ب" من الصفحة "أ" ، ويتم تنظيف البيانات الموجودة في النموذج "أ" واستعادتها إلى الحالة الأولية.

السلوك الفعلي: (التأثير الفعلي)

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

هل يمكنني إلغاء التأثيرات المحددة عندما أغادر الصفحة "أ" ، أو إلغاء جميع التأثيرات في مساحة الاسم؟ (على غرار إلغاء في redux-saga)

إصدارات الحزم المستخدمة: (أي إصدار من المكتبة به المشكلة)

الإصدار 2.2.3

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

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

import { getProduct } from '@/services/Products';
import { isRespSucc, showErrorMsg } from '@/utils/utils';

const initState = {};

export default {
  namespace: 'product',

  state: initState,

  effects: {
    /**
      在两个 Effects 之间触发一个竞赛(race)
      如果task先结束,竞赛结束。
      如果task未结束时收到cancel,race effect 将自动取消 task。
    */
    *cancelable({ task, payload }, { call, race, take }) {
      yield race({
        task: call(task, payload),
        cancel: take('cancel'),
      });
    },

    /**
      取消所有未完成的任务,并执行数据清理
    */
    *clear(_, { put }) {
      yield put.resolve({ type: 'cancel' });
      yield put({ type: 'clearState' });
    },

    *getProduct({ payload }, { call, put, cancelled }) {
      // eslint-disable-next-line
      yield put.resolve({ type: 'cancelable', task: getProductCancelable, payload });

      function* getProductCancelable(params) {
        try {
          // 调用网络请求
          const response = yield call(getProduct, params);
          // 返回结果判断
          if (!isRespSucc(response)) {
            showErrorMsg(response);
            return;
          }
          // 取值
          const { productName } = response.data;
          // 调用reducer存值
          yield put({
            type: 'saveState',
            payload: { productName },
          });
        } finally {
          if (yield cancelled()) {
            // TODO: 取消网络请求
          }
        }
      }
    },
    *getCity(_, { call, put, cancelled }) {
      // eslint-disable-next-line
      yield put.resolve({ type: 'cancelable', task: getCityCancelable });

      function* getCityCancelable() {
        // TODO: 具体实现
      }
  },

  reducers: {
    saveState(state, { payload }) {
      const newState = { ...state, ...payload };
      return newState;
    },
    clearState() {
      return initState;
    },
  },
};


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

  componentWillMount() {
    const { dispatch } = this.props;
    dispatch({
      type: 'product/getProduct',
      payload: {
        productNumber: '123456',
      },
    });
    dispatch({
      type: 'product/getCity',
    });
  }

  componentWillUnmount() {
    const { dispatch } = this.props;
    dispatch({
      type: 'product/clear',
    });
  }

  function mapStateToProps(state) {
    return {
      getProductLoading: state.loading.effects['product/getProduct'],
      getCityLoading: state.loading.effects['product/getCity'],
    };
  }

ال 11 كومينتر

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

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

لقد واجهت مشكلة الملصق الأصلي. أردت إلغاء التأثير غير المكتمل عند إلغاء تثبيت المكون ، لكنني وجدت أنه لا توجد طريقة. لا يبدو أن Dva يقدم هذا النوع من apisorrycc

هل من الممكن إلغاء التأثيرات وفصل الملاحم

واجه dlamon نفس الموقف وأراد التنظيف عندما تم تبديل المسار. هل يوجد حل الآن؟

من غير المتوقع أن يتم تنظيف

يتمثل أسلوبي الحالي في إنشاء منطقة بيانات فرعية باستخدام UUID في منطقة بيانات النموذج المقابلة للمعاملة الحالية في كل مرة أدخل فيها معاملة (عندما أدخل componentWillMount) ، والذي يتم استخدامه لتخزين البيانات المستخدمة في المعاملة الحالية. تتم كتابة UUID ويتم مسحها عند خروج المعاملة (componentWillUnmount). على غرار هيكل النموذج أدناه:
2018-11-07 7 25 31
إذا عاد الاتصال بعد النموذج الواضح ، عند كتابة البيانات ، نظرًا لأن UUID المستخدم بواسطة المخفض الحالي هو UUID المستخدم في المعاملة السابقة ، ستتم كتابة البيانات القذرة في منطقة بيانات المعاملة السابقة ، وسوف لا يتم إنشاؤها للمعاملة التالية.

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

dlamon لقد ببعض المشاكل https://codesandbox.io/s/yqwqpmvwvj

إلغاء namespace إلى products إلى model في طريقة effect :
dispatch({ type: 'products/@<strong i="10">@CANCEL_EFFECTS</strong>' });

هل هذا الرمز

        yield sagaEffects.fork(function*() {
          yield sagaEffects.take(`${model.namespace}/@@CANCEL_EFFECTS`);
          yield sagaEffects.cancel(task);
        });

@ wss1942 شكرًا لك على الرد على هذه الطريقة. لقد @CANCEL_EFFECTS '}) سيتسبب في أن التأثير في الشكل المقابل غير صالح. على غرار # 796

لا يبدو أن dlamon dva يوفر واجهة برمجة تطبيقات لمسح التأثير. ومع ذلك ، يمكن كتابة العديد من الملاحم بالتأثير المحدد في النموذج.

 namespace: 'products',
 effects: {
  *start(){},
  *stop(){},
  watchLogin: [
      function* ({ take, put, call, cancel, fork, cancelled }) {
          yield take('start');
          const timerTask = yield fork(timer)
          const bgSyncTask = yield fork(bgSync)
          yield take('stop')
          yield cancel(bgSyncTask)
          yield cancel(timerTask)

        function* bgSync() {
          try {
            while (true) {
              const result = yield call(delay, 5 * 1000);
              yield put({ type: 'stop' })
            }
          } finally {
            if (yield cancelled())
              yield put({ type: 'log', payload: 'fetch🛑' })
          }
        }
        function* timer(time) {
          let i=0;
          while (true) {
            yield put({ type: 'log', payload: i++ })
            yield delay(1000)
          }
        }
      },
      { type: 'watcher' },
    ],
}

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

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

import { getProduct } from '@/services/Products';
import { isRespSucc, showErrorMsg } from '@/utils/utils';

const initState = {};

export default {
  namespace: 'product',

  state: initState,

  effects: {
    /**
      在两个 Effects 之间触发一个竞赛(race)
      如果task先结束,竞赛结束。
      如果task未结束时收到cancel,race effect 将自动取消 task。
    */
    *cancelable({ task, payload }, { call, race, take }) {
      yield race({
        task: call(task, payload),
        cancel: take('cancel'),
      });
    },

    /**
      取消所有未完成的任务,并执行数据清理
    */
    *clear(_, { put }) {
      yield put.resolve({ type: 'cancel' });
      yield put({ type: 'clearState' });
    },

    *getProduct({ payload }, { call, put, cancelled }) {
      // eslint-disable-next-line
      yield put.resolve({ type: 'cancelable', task: getProductCancelable, payload });

      function* getProductCancelable(params) {
        try {
          // 调用网络请求
          const response = yield call(getProduct, params);
          // 返回结果判断
          if (!isRespSucc(response)) {
            showErrorMsg(response);
            return;
          }
          // 取值
          const { productName } = response.data;
          // 调用reducer存值
          yield put({
            type: 'saveState',
            payload: { productName },
          });
        } finally {
          if (yield cancelled()) {
            // TODO: 取消网络请求
          }
        }
      }
    },
    *getCity(_, { call, put, cancelled }) {
      // eslint-disable-next-line
      yield put.resolve({ type: 'cancelable', task: getCityCancelable });

      function* getCityCancelable() {
        // TODO: 具体实现
      }
  },

  reducers: {
    saveState(state, { payload }) {
      const newState = { ...state, ...payload };
      return newState;
    },
    clearState() {
      return initState;
    },
  },
};


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

  componentWillMount() {
    const { dispatch } = this.props;
    dispatch({
      type: 'product/getProduct',
      payload: {
        productNumber: '123456',
      },
    });
    dispatch({
      type: 'product/getCity',
    });
  }

  componentWillUnmount() {
    const { dispatch } = this.props;
    dispatch({
      type: 'product/clear',
    });
  }

  function mapStateToProps(state) {
    return {
      getProductLoading: state.loading.effects['product/getProduct'],
      getCityLoading: state.loading.effects['product/getCity'],
    };
  }
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات