Redux: Middleware ist defekt

Erstellt am 6. Juni 2018  ·  3Kommentare  ·  Quelle: reduxjs/redux

Insekt

Ein wichtiges Middleware-Muster wurde durchbrochen. IMO kapseln die meisten Middleware-Lösungen ihre eigenen Anliegen und sind unabhängig von der Existenz anderer Middleware. Kürzlich wurde eine Änderung vorgenommen, um ein Muster zu durchbrechen, das nur ein geringfügiges Problem für eine bestimmte Untergruppe von Middleware verursachte: diejenigen, die bei der Interaktion mit anderer Middleware während des applyMiddleware Initialisierungspfads ein anderes Verhalten zeigen.

Hier sind einige Beispiele:

// Make state aware of browser media queries
const mediaQueries = (mediaQueries = defaultMediaQueries): Middleware => store => {
  if (typeof window !== 'undefined') {
    const reverseMap = createReverseMap(mediaQueries)

    const handleMediaChange = ({media, matches}: MediaChangedActionPayload) =>
      store.dispatch(mediaChanged(reverseMap[media], matches))

    const addMatchListener = (media: string) => {
      const match = window.matchMedia(media)
      match.addListener(handleMediaChange)
      return match.matches
    }

    values(mediaQueries).forEach(media => 
      handleMediaChange({media, matches: addMatchListener(media)}))
  }

  return next => (action: any) => next(action)
}

Ein anderes Beispiel:

// Make state aware of user adblockers
const adBlockDetection: Middleware = store => {
  if (typeof document !== 'undefined' && !document.getElementById('XMpADSwgbPUC')) {
    store.dispatch({type: AD_BLOCKER_DETECTED, payload: true})
  }
  return next => (action: any) => next(action)
}

Ein anderes Beispiel:

// Make state aware of socket connectivity and allow synchronisation of actions
const socketMiddleware = ({actionCreator, url, dataSync}) => store => {
  const socket = new WebSocket(url.replace(/(http|https)/, 'ws'))

  socket.addEventListener('message', ({data}) => {
    store.dispatch(JSON.parse(data))
  })

  socket.addEventListener('open', () => {
    socket.send(syncCreator(CONNECT_TO_DOMAIN, store.getState(), null, socket))
    store.dispatch(actionCreator({connected: true}))
  })

  socket.addEventListener('close', reason => {
    store.dispatch(actionCreator({connected: false}))
  })

  return next => action => {
    if (dataSync.hasOwnProperty(action.type)) {
      socket.send(syncCreator(action.type, store.getState(), action.payload, socket))
    }
    return next(action)
  }
}

Wie ist das aktuelle Verhalten?

Der folgende Fehler wird während applyMiddleware ausgegeben, wodurch die Anwendung im Wesentlichen unterbrochen wird:

Uncaught Error: Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch

Das Refactoring der obigen Middleware, die durch die next => Funktion geschlossen wird (wie unten), macht keinen Unterschied (ist aber offensichtlich auch keine praktikable Lösung).

// Still broken:
const mediaQueries = (mediaQueries = defaultMediaQueries): Middleware => store => next => {
  // ...middlewareCodeGoesHere...
  return (action: any) => next(action)
}

Was ist das erwartete Verhalten?

Mach die App nicht kaputt. Protokollieren Sie eine Warnung, wenn Sie möchten, damit ich sie ignorieren kann, denn in 100% meiner Anwendungsfälle ist es mir egal, ob andere Middleware diesen Versand handhaben kann_. Ich vermute stark, dass es sehr wenige legitime Gründe gibt (oder geben sollte), warum ein Dispatch aus einem Middleware-Kontext jemals mit einer anderen Middleware im Stack interagieren sollte, und wenn dies der Fall wäre, würde ich dies als Code-Geruch betrachten (es würde zumindest implizite Reihenfolgenabhängigkeiten von Middleware).

Welche Redux-Versionen und welche Browser und Betriebssysteme sind von diesem Problem betroffen?

Hat bis zu dieser PR funktioniert .

Hilfreichster Kommentar

@markerikson Ja, das Problem habe ich gesehen. Das Hauptproblem, das das neue Design "löst", ist folgendes:

Hier ist das Problem: Dieser Listener wird ausgelöst, wenn Sie die Middleware anwenden. Der Aufruf von

Dies ist IMO, ein Implementierungsdetail, mit dem der Anwendungsentwickler umgehen sollte, und nicht etwas, das _die Funktionsweise der meisten Middleware beeinträchtigen sollte_. Mit einer Warnung anstelle eines Fehlers kann der Anwendungsentwickler das Problem identifizieren und seine eigene Middleware umgestalten, um es zu beheben.

Im aktuellen Zustand ist alle vorhandene Middleware, einschließlich Middleware von Drittanbietern, die dieses interne Dispatch-Muster verwendet (im Grunde alles, was Listener für alles instanziieren muss), defekt.

Bei 3.x zu bleiben ist leider keine Option für uns, da wir Typescript verwenden und nur 4.x die richtigen Typisierungen hat und wir im Allgemeinen lieber nicht bei einer Legacy-Version eines Kernteils unseres stecken bleiben Anwendungslogik, auch wenn Eingaben kein Problem darstellen.

Und meiner Meinung nach weist schon die Fehlermeldung selbst darauf hin, wie unwichtig sie eigentlich ist: Das "Problem" ist, dass Other middleware would not be applied to this dispatch , was eigentlich nur sehr selten gewünscht wird und bisher nur ein konkretes Thema war Anwendungsfall, bei dem die Analyse-Middleware von Aktionen abhängig war, die von einem Router gesendet wurden.

Es ist wichtig zu beachten, dass der "Fix" in Redux jetzt auch den Code zerstört, der repariert werden sollte . Es bricht die Router-Middleware. Jetzt müssen Autoren dieser Middleware (und aller anderen ähnlichen Middleware) 4.x-kompatible Versionen ihrer Bibliotheken veröffentlichen, die nun die Implementierungsdetails einer manuell gesendeten "store ready"-Aktion offenlegen müssen, die der Verbraucher implementieren muss.

Die Verwendung von Middleware-Bibliotheken hat sich vom einfachen Importieren des Pakets und dem Einfügen in das Middleware-Array geändert, sodass App-Autoren jetzt zusätzlich einen hackigen Weg finden müssen, um eine beliebige "store ready" -Aktion einige Zeit nach der Erstellung des Stores auszuführen, aber bevor es verwendet wird. Und jetzt muss hoffentlich ALLE ihre Middleware denselben Aktionstyp verwenden, ansonsten müssen App-Autoren mehrere "store ready" -Aktionen versenden, wobei verschiedene Typen mit der von den Middleware-Autoren implementierten API übereinstimmen.

Dies ist ein echtes Durcheinander, und die "Heilung" ist in diesem Fall eindeutig viel schlimmer als die "Krankheit".

Alle 3 Kommentare

Der Middleware-Init-Prozess wurde in 4.0 in der von Ihnen verlinkten PR explizit geändert. Dies war eine bewusste Designentscheidung und sollte #1240 lösen.

Ich bin mir nicht sicher, ob ich einen sofortigen Vorschlag für Ihren Anwendungsfall habe, außer zu versuchen, die Logik Ihrer Middleware so zu ändern, dass sie auf eine manuell ausgelöste Aktion "Speicher ist bereit" wartet. Sie könnten auch überlegen, stattdessen bei 3.x zu bleiben.

@markerikson Ja, das Problem habe ich gesehen. Das Hauptproblem, das das neue Design "löst", ist folgendes:

Hier ist das Problem: Dieser Listener wird ausgelöst, wenn Sie die Middleware anwenden. Der Aufruf von

Dies ist IMO, ein Implementierungsdetail, mit dem der Anwendungsentwickler umgehen sollte, und nicht etwas, das _die Funktionsweise der meisten Middleware beeinträchtigen sollte_. Mit einer Warnung anstelle eines Fehlers kann der Anwendungsentwickler das Problem identifizieren und seine eigene Middleware umgestalten, um es zu beheben.

Im aktuellen Zustand ist alle vorhandene Middleware, einschließlich Middleware von Drittanbietern, die dieses interne Dispatch-Muster verwendet (im Grunde alles, was Listener für alles instanziieren muss), defekt.

Bei 3.x zu bleiben ist leider keine Option für uns, da wir Typescript verwenden und nur 4.x die richtigen Typisierungen hat und wir im Allgemeinen lieber nicht bei einer Legacy-Version eines Kernteils unseres stecken bleiben Anwendungslogik, auch wenn Eingaben kein Problem darstellen.

Und meiner Meinung nach weist schon die Fehlermeldung selbst darauf hin, wie unwichtig sie eigentlich ist: Das "Problem" ist, dass Other middleware would not be applied to this dispatch , was eigentlich nur sehr selten gewünscht wird und bisher nur ein konkretes Thema war Anwendungsfall, bei dem die Analyse-Middleware von Aktionen abhängig war, die von einem Router gesendet wurden.

Es ist wichtig zu beachten, dass der "Fix" in Redux jetzt auch den Code zerstört, der repariert werden sollte . Es bricht die Router-Middleware. Jetzt müssen Autoren dieser Middleware (und aller anderen ähnlichen Middleware) 4.x-kompatible Versionen ihrer Bibliotheken veröffentlichen, die nun die Implementierungsdetails einer manuell gesendeten "store ready"-Aktion offenlegen müssen, die der Verbraucher implementieren muss.

Die Verwendung von Middleware-Bibliotheken hat sich vom einfachen Importieren des Pakets und dem Einfügen in das Middleware-Array geändert, sodass App-Autoren jetzt zusätzlich einen hackigen Weg finden müssen, um eine beliebige "store ready" -Aktion einige Zeit nach der Erstellung des Stores auszuführen, aber bevor es verwendet wird. Und jetzt muss hoffentlich ALLE ihre Middleware denselben Aktionstyp verwenden, ansonsten müssen App-Autoren mehrere "store ready" -Aktionen versenden, wobei verschiedene Typen mit der von den Middleware-Autoren implementierten API übereinstimmen.

Dies ist ein echtes Durcheinander, und die "Heilung" ist in diesem Fall eindeutig viel schlimmer als die "Krankheit".

Es ist immer noch möglich, jedes Ihrer Beispiele zum Laufen zu bringen. Einfach nach applyMiddleware versenden. Vielleicht in einer Art setupMiddleware(store) Funktion oder einem Shop-Enhancer.

Der Fehler wurde absichtlich als Alternative eingeführt, um Benutzern zu ermöglichen, einen Grenzfall zu treffen, der eine sehr nicht offensichtliche Ursache hat (es sei denn, Sie sind mit den Interna von applyMiddleware vertraut). Redux vertritt die Haltung, Benutzerfehler wann immer möglich zu verhindern, so dass dies nicht verschwindet.

Eine Alternative könnte ein Callback sein, den die Middleware als eine Art afterApply Ereignis registrieren kann. Dann versenden Sie garantiert zum richtigen Zeitpunkt, nachdem alles aufgetragen und in Ordnung ist. Das wäre interessant zu durchdenken.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen