Redux: Le middleware est cassé

Créé le 6 juin 2018  ·  3Commentaires  ·  Source: reduxjs/redux

Bogue

Un important modèle de middleware a été rompu. OMI, la majorité des solutions middleware encapsulent leurs propres préoccupations et sont indépendantes de l'existence d'autres middleware. Une modification a été apportée récemment pour casser un modèle qui ne causait qu'un problème mineur pour un sous-ensemble spécifique de middleware : ceux qui présentent un comportement différent lors de l'interaction avec d'autres middleware pendant le chemin d'initialisation applyMiddleware .

Voici quelques exemples:

// 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)
}

Un autre exemple:

// 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)
}

Un autre exemple:

// 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)
  }
}

Quel est le comportement actuel ?

L'erreur suivante est générée pendant applyMiddleware , ce qui interrompt essentiellement l'application :

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

Refactoriser le middleware ci-dessus pour qu'il soit fermé par la fonction next => (comme ci-dessous) ne fait aucune différence (mais n'est évidemment pas une solution viable non plus).

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

Quel est le comportement attendu ?

Ne cassez pas l'application. Enregistrez un avertissement si vous le souhaitez, afin que je puisse l'ignorer, car dans 100% de mes cas d'utilisation, je me fiche que d'autres middlewares puissent gérer cette répartition. Je soupçonne fortement qu'il y a (ou qu'il devrait y avoir) très peu de raisons légitimes pour lesquelles un envoi à partir d'un contexte middleware devrait jamais interagir avec un autre middleware de la pile, et si c'était le cas, je considérerais qu'un code sentait (cela placerait dépendances d'ordre implicites sur le middleware à tout le moins).

Quelles versions de Redux, et quel navigateur et système d'exploitation sont concernés par ce problème ?

A travaillé jusqu'à ce PR .

Commentaire le plus utile

@markerikson Oui, j'ai vu ce problème. Le problème clé que la nouvelle conception « résout » est le suivant :

Voici le problème : cet écouteur se déclenche lorsque vous appliquez le middleware, donc appeler store.dispatch appelle le dispatch sans middleware et notre middleware d'analyse ne verra aucun événement .

Il s'agit de l'IMO, un détail d'implémentation que le développeur d'applications doit gérer, pas quelque chose qui devrait _casser le fonctionnement de la plupart des middleware_. Avec un avertissement au lieu de générer une erreur, le développeur de l'application peut identifier le problème et refactoriser son propre middleware pour le résoudre.

Dans son état actuel, tous les middlewares existants, y compris les middlewares tiers, qui utilisent ce modèle de répartition interne (essentiellement tout ce qui doit instancier des auditeurs sur quoi que ce soit), sont cassés.

Rester avec 3.x n'est malheureusement pas une option pour nous, car nous utilisons Typescript, et seul 4.x a les typages corrects, et en général, nous préférons ne pas être coincés avec une version héritée d'une partie centrale de notre logique de l'application même si les frappes n'étaient pas un problème.

Et à mon avis, même le message d'erreur lui-même indique à quel point c'est vraiment sans importance : le "problème" est que Other middleware would not be applied to this dispatch , ce qui est très rarement réellement souhaité, et jusqu'à présent, n'a été un problème que pour un cas d'utilisation, où le middleware d'analyse dépendait des actions envoyées à partir d'un routeur.

Il est important de noter que le "correctif" dans redux casse actuellement

L'utilisation des bibliothèques middleware a changé, passant de la simple importation du package et de l'insertion dans le tableau des middlewares, à désormais exiger en plus des auteurs d'applications qu'ils trouvent un moyen piraté d'envoyer une action arbitraire "prêt pour le magasin" quelque temps après la fin de la création du magasin, mais avant qu'il ne soit utilisé. Et maintenant, TOUS leurs middlewares doivent, espérons-le, adhérer au même type d'action, sinon les auteurs d'applications devront envoyer plusieurs actions "prêtes pour le magasin", avec différents types correspondant à l'API que les auteurs de middleware ont implémentée.

C'est un vrai gâchis, et le « remède » est clairement bien pire que la « maladie » dans ce cas.

Tous les 3 commentaires

Le processus d'initialisation du middleware a été explicitement modifié dans 4.0 dans le PR que vous avez lié. Il s'agissait d'une décision de conception délibérée et destinée à résoudre le n° 1240.

Je ne suis pas sûr d'avoir une suggestion immédiate pour votre cas d'utilisation, autre que d'essayer de changer la logique de votre middleware pour attendre une action "le magasin est prêt" envoyée manuellement. Vous pouvez également envisager de rester avec 3.x à la place.

@markerikson Oui, j'ai vu ce problème. Le problème clé que la nouvelle conception « résout » est le suivant :

Voici le problème : cet écouteur se déclenche lorsque vous appliquez le middleware, donc appeler store.dispatch appelle le dispatch sans middleware et notre middleware d'analyse ne verra aucun événement .

Il s'agit de l'IMO, un détail d'implémentation que le développeur d'applications doit gérer, pas quelque chose qui devrait _casser le fonctionnement de la plupart des middleware_. Avec un avertissement au lieu de générer une erreur, le développeur de l'application peut identifier le problème et refactoriser son propre middleware pour le résoudre.

Dans son état actuel, tous les middlewares existants, y compris les middlewares tiers, qui utilisent ce modèle de répartition interne (essentiellement tout ce qui doit instancier des auditeurs sur quoi que ce soit), sont cassés.

Rester avec 3.x n'est malheureusement pas une option pour nous, car nous utilisons Typescript, et seul 4.x a les typages corrects, et en général, nous préférons ne pas être coincés avec une version héritée d'une partie centrale de notre logique de l'application même si les frappes n'étaient pas un problème.

Et à mon avis, même le message d'erreur lui-même indique à quel point c'est vraiment sans importance : le "problème" est que Other middleware would not be applied to this dispatch , ce qui est très rarement réellement souhaité, et jusqu'à présent, n'a été un problème que pour un cas d'utilisation, où le middleware d'analyse dépendait des actions envoyées à partir d'un routeur.

Il est important de noter que le "correctif" dans redux casse actuellement

L'utilisation des bibliothèques middleware a changé, passant de la simple importation du package et de l'insertion dans le tableau des middlewares, à désormais exiger en plus des auteurs d'applications qu'ils trouvent un moyen piraté d'envoyer une action arbitraire "prêt pour le magasin" quelque temps après la fin de la création du magasin, mais avant qu'il ne soit utilisé. Et maintenant, TOUS leurs middlewares doivent, espérons-le, adhérer au même type d'action, sinon les auteurs d'applications devront envoyer plusieurs actions "prêtes pour le magasin", avec différents types correspondant à l'API que les auteurs de middleware ont implémentée.

C'est un vrai gâchis, et le « remède » est clairement bien pire que la « maladie » dans ce cas.

Il est toujours possible de faire fonctionner chacun de vos exemples. Il suffit d'envoyer après l'applicationMiddleware. Peut-être dans une sorte de fonction setupMiddleware(store) ou un amplificateur de magasin.

L'erreur a été intentionnellement introduite car l'alternative est de permettre aux utilisateurs d'atteindre un cas limite qui a une cause très peu évidente (à moins que vous ne soyez familier avec les internes de applyMiddleware ). Redux prend pour position d'empêcher les erreurs de l'utilisateur dans la mesure du possible, donc cela ne va pas disparaître.

Une alternative pourrait être une sorte de rappel que le middleware peut enregistrer comme une sorte d'événement afterApply . Vous serez alors assuré d'expédier au bon moment, une fois que tout est appliqué et en ordre. Ce serait intéressant d'y réfléchir.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

CellOcean picture CellOcean  ·  3Commentaires

captbaritone picture captbaritone  ·  3Commentaires

olalonde picture olalonde  ·  3Commentaires

jbri7357 picture jbri7357  ·  3Commentaires

wmertens picture wmertens  ·  4Commentaires