Dva: Effekte abbrechen

Erstellt am 8. Juni 2018  ·  11Kommentare  ·  Quelle: dvajs/dva

Code zum Reproduzieren des Problems: (Bitte geben Sie reproduzierbaren Code oder reproduzierbare Schritte an)

Szenario: Wenn es zwei Seiten A und B im Projekt gibt, die Modell A und Modell B entsprechen, gibt es auf jeder Seite einige asynchrone Netzwerkanforderungen, die in Effekten initiiert werden und die zurückgegebenen Daten werden über den Reducer in das aktuelle Modell geschrieben Zustand.

Wenn ich Seite A oder Seite B verlasse, möchte ich die Daten im entsprechenden Modell löschen, damit sie bei der nächsten Wiederverwendung nicht zu schmutzigen Daten werden.
Ich habe einen klaren Reduzierer in das Modell geschrieben, in der Methode componentWillUnmount
Versand({ type:${model.namespace}/clear})

Erwartetes Verhalten: (erwarteter normaler Effekt)

Der Benutzer betritt Seite B von Seite A, und die Daten in Modell A werden bereinigt und in den Anfangszustand zurückversetzt.

Tatsächliches Verhalten: (tatsächliche Wirkung)

Wenn der Benutzer Seite B von Seite A aus betritt, wenn eine asynchrone Netzwerkanforderung auf Seite A auftritt und Seite B aufruft, bevor die Netzwerkanforderung abgeschlossen ist, löscht Seite A zuerst die Modell-A-Daten durch den Clear Reducer, aber wenn das Netzwerk aktiv ist Wenn die Anfrage abgeschlossen ist, werden die zurückgegebenen Daten in Modell A umgeschrieben, was zu schmutzigen Daten aus dem vorherigen Geschäft in Modell A führt.

Kann ich die angegebenen Effekte abbrechen, wenn ich die A-Seite verlasse, oder alle Effekte in einem Namespace abbrechen? (Ähnlich wie in Redux-Saga stornieren)

Verwendete Versionen von Paketen: (welche Version welcher Bibliothek hat das Problem)

v2.2.3

Hilfreichster Kommentar

@wss1942 Danke!
Auf diese Weise können Effekte abgebrochen werden. Es ist mühsamer, den Ladestatus anzuzeigen. Sie können nicht direkt die loading.effects verwenden, die mit dva geliefert werden, um den Ladestatus anzuzeigen. Sie müssen Ihren eigenen Code schreiben, um ihn zu ändern.
Ich habe es modifiziert und einen vollständigen Modellcode gepostet

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;
    },
  },
};


Beim Verlassen der Transaktion (componentWillUnmount) kann Dispatch clear alle unfertigen Effekte im aktuellen Modell abbrechen und auch getProductLoading und getCityLoading können normal verwendet werden.

  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'],
    };
  }

Alle 11 Kommentare

Sie können die Routingänderungen in den Abonnements des Modells überwachen und den Status von Modell A manuell löschen, wenn die Seite eine Nicht-A-Seite (A-Seite verlassen) oder A-Seite (A-Seite eingeben) ist.

Danke für die Antwort. Ich habe diese Methode ausprobiert. Es gibt kein Problem beim Löschen des Zustands selbst. Es kann entweder in componentWillUnmount gelöscht werden oder wenn Abonnements Routingänderungen überwachen. Das Problem ist, dass nach dem Löschen des modalen Zustands die Daten von der asynchronen Operation in den Effekten zurückgegeben werden Es wird weiterhin in den modalen Zustand umgeschrieben, was zu schmutzigen Daten aus dem vorherigen Geschäft führt.
Ich überwache sogar die Routing-Änderungen in Abonnements. Wenn ich Seite A verlasse, verwende ich unmodel, um Modell A zu deinstallieren. Es ist sinnlos, Seite A aufzurufen und Modell A manuell zu laden. die Daten werden eingeschrieben, unabhängig davon, ob dieses Modell das letzte vom Unternehmen verwendete oder das neu geladene Modell ist.
Der beste Weg ist also, den Zustand im Modell zu löschen und alle Effekte im aktuellen Namespace aufzuheben, aber es wird keine aufzurufende Methode gefunden. Rat suchen.

Ich bin auf das Problem des Originalposters gestoßen. Ich wollte den unvollendeten Effekt abbrechen, als die Komponente deinstalliert wurde, aber ich fand, dass es keine Möglichkeit gab. Dva scheint diese Art von API nicht bereitzustellen @sorrycc

Ist es möglich, die Effekte aufzuheben und die Sagen zu trennen?

@dlamon hatte die gleiche Situation und wollte aufräumen, als die Route gewechselt wurde. Gibt es jetzt eine Lösung?

Es wird nicht erwartet, dass Routenwechsels aufräumt , da die Kommunikation nach Abschluss des wiederkehrt und die schmutzigen Daten nach der Rückkehr der Kommunikation weiterhin in den bereinigten Modelldatenbereich zurückgeschrieben werden.

Mein derzeitiger Ansatz besteht darin, jedes Mal, wenn ich eine Transaktion eingebe (bei ComponentWillMount), einen Unterdatenbereich mit UUID im Modelldatenbereich entsprechend der aktuellen Transaktion zu generieren, in dem die in der aktuellen Transaktion verwendeten Daten gespeichert werden und gelöscht, wenn die Transaktion beendet wird (componentWillUnmount). Ähnlich der Modellstruktur unten:
2018-11-07 7 25 31
Wenn die Kommunikation nach dem Clear-Modell beim Schreiben von Daten zurückkommt, werden die schmutzigen Daten in den Datenbereich der vorherigen Transaktion geschrieben, da die vom aktuellen Reduzierer verwendete UUID die UUID ist, die in der vorherigen Transaktion verwendet wurde nicht für die nächste Transaktion generiert werden.

Aber auch dieses Verfahren ist nicht gut: Erstens erhöht es die logische Komplexität, und zweitens erfordert das Erhalten des Anfangswerts das Hinzufügen einer Nullwertbeurteilung, was die Komplexität des Codes erhöht. Da ich hauptsächlich für das Finanzsystem arbeite, habe ich Angst vor dieser Art von schmutzigen Daten, also wähle ich diese Methode.
Ich denke, der beste Weg ist ähnlich dem Mechanismus zum Abbrechen von Effekten in Redux-Saga, aber dva scheint ihn jetzt nicht zu unterstützen.
Wenn Sie eine bessere Lösung haben, bitte @我

@dlamon Ich bin auch darauf https://codesandbox.io/s/yqwqpmvwvj

Abbrechen einer namespace bis products bis model in effect Methode:
dispatch({ type: 'products/@<strong i="10">@CANCEL_EFFECTS</strong>' });

Ist dieser Code

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

@wss1942 Vielen Dank, dass Sie auf diese Methode @CANCEL_EFFECTS '}) dazu führt, dass der Effekt im entsprechenden Modal ungültig ist. Ähnlich wie #796

@dlamon dva scheint keine API bereitzustellen, um einen Effekt zu löschen. Es können jedoch mehrere Sagen in den im Modell definierten Effekt geschrieben werden.

 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 ist Ihre asynchrone Aufgabe, Sie können die Aufgabe mit action: starten und mit action:stop abbrechen . Dies kann erreicht werden, um einen bestimmten Effekt aufzuheben.
Wenn es sich bei der asynchronen Aufgabe um eine Netzwerkanforderung handelt, kann außerdem eine Operation zum Abbrechen der Netzwerkanforderung erforderlich sein, beispielsweise kann axios mit axios.CancelToken abgebrochen werden.

@wss1942 Danke!
Auf diese Weise können Effekte abgebrochen werden. Es ist mühsamer, den Ladestatus anzuzeigen. Sie können nicht direkt die loading.effects verwenden, die mit dva geliefert werden, um den Ladestatus anzuzeigen. Sie müssen Ihren eigenen Code schreiben, um ihn zu ändern.
Ich habe es modifiziert und einen vollständigen Modellcode gepostet

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;
    },
  },
};


Beim Verlassen der Transaktion (componentWillUnmount) kann Dispatch clear alle unfertigen Effekte im aktuellen Modell abbrechen und auch getProductLoading und getCityLoading können normal verwendet werden.

  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'],
    };
  }
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen