Redux: Middleware está roto

Creado en 6 jun. 2018  ·  3Comentarios  ·  Fuente: reduxjs/redux

Insecto

Se ha roto un patrón de middleware importante. En mi opinión, la mayoría de las soluciones de middleware encapsulan sus propias preocupaciones y son independientes de la existencia de otro middleware. Recientemente se realizó un cambio para romper un patrón que solo causó un problema menor para un subconjunto específico de middleware: aquellos que exhiben un comportamiento diferente al interactuar con otro middleware durante la ruta de inicialización applyMiddleware .

Aquí hay unos ejemplos:

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

Otro ejemplo:

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

Otro ejemplo:

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

¿Cuál es el comportamiento actual?

El siguiente error se produce durante applyMiddleware , esencialmente rompiendo la aplicación:

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

Refactorizar el middleware anterior para cerrarlo con la función next => (como a continuación) no hace ninguna diferencia (pero obviamente tampoco es una solución viable).

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

¿Cuál es el comportamiento esperado?

No rompa la aplicación. Registre una advertencia si lo desea, para que pueda ignorarla, porque en el 100% de mis casos de uso _No me importa si otro middleware puede manejar ese envío_. Sospecho firmemente que hay (o debería haber) muy pocas razones legítimas por las que cualquier envío desde un contexto de middleware debería interactuar con cualquier otro middleware en la pila, y si lo hiciera, consideraría que un código huele (colocaría dependencias de orden implícitas en el middleware como mínimo).

¿Qué versiones de Redux y qué navegador y sistema operativo se ven afectados por este problema?

Trabajó hasta este PR .

Comentario más útil

@markerikson Sí, he visto ese problema. El problema clave que el nuevo diseño "resuelve" es este:

Aquí está el problema: ese oyente se activa cuando está aplicando el middleware, por lo que llamar a store.dispatch es llamar al despacho no intermediario y nuestro middleware de análisis no verá ningún evento .

Esto es IMO, un detalle de implementación que debe manejar el desarrollador de la aplicación, no algo que deba _ romper cómo funciona la mayoría de los middleware_. Con una advertencia en lugar de arrojar un error, el desarrollador de la aplicación puede identificar el problema y refactorizar su propio middleware para solucionarlo.

En su estado actual, todo el middleware existente, incluido el middleware de terceros, que utiliza este patrón de despacho interno (básicamente cualquier cosa que necesite instanciar oyentes en cualquier cosa), está roto.

Lamentablemente, permanecer con 3.x no es una opción para nosotros, ya que usamos TypeScript, y solo 4.x tiene los tipos correctos y, en general, preferimos no quedarnos atascados con una versión heredada de una parte central de nuestro lógica de la aplicación incluso si las mecanografías no fueran un problema.

Y en mi opinión, incluso el mensaje de error en sí mismo insinúa cuán poco importante es realmente: el "problema" es que Other middleware would not be applied to this dispatch , que en realidad rara vez se desea, y hasta ahora, solo ha sido un problema para un caso de uso, donde el middleware de análisis dependía de las acciones enviadas desde un enrutador.

Es importante tener en cuenta que la "corrección" en redux ahora rompe el código que se pretendía arreglar también . Rompe el middleware del enrutador. Ahora los autores de ese middleware (y todos los demás middleware similares) necesitan lanzar versiones compatibles con 4.x de sus bibliotecas, que ahora necesitan exponer los detalles de implementación de una acción "store ready" enviada manualmente, que el consumidor debe implementar.

El uso de bibliotecas de middleware ha cambiado de simplemente importar el paquete e insertarlo en la matriz de middlewares, a ahora, además, se requiere que los autores de la aplicación encuentren alguna forma pirata de enviar una acción arbitraria de "tienda lista" algún tiempo después de que la tienda haya terminado de crearse, pero antes de que se utilice. Y ahora TODO su middleware debe adherirse al mismo tipo de acción, de lo contrario, los autores de aplicaciones necesitarán enviar múltiples acciones "listas para almacenar", con diferentes tipos que coincidan con cualquier API que implementaron los autores de middleware.

Esto es un verdadero lío, y la "cura" es claramente mucho peor que la "enfermedad" en este caso.

Todos 3 comentarios

El proceso de inicio de middleware se cambió explícitamente en 4.0 en el PR que vinculó. Esta fue una decisión de diseño deliberada y tenía la intención de resolver el # 1240.

No estoy seguro de tener una sugerencia inmediata para su caso de uso, aparte de intentar cambiar la lógica de su middleware para esperar alguna acción "la tienda está lista" enviada manualmente. También podría considerar quedarse con 3.x en su lugar.

@markerikson Sí, he visto ese problema. El problema clave que el nuevo diseño "resuelve" es este:

Aquí está el problema: ese oyente se activa cuando está aplicando el middleware, por lo que llamar a store.dispatch es llamar al despacho no intermediario y nuestro middleware de análisis no verá ningún evento .

Esto es IMO, un detalle de implementación que debe manejar el desarrollador de la aplicación, no algo que deba _ romper cómo funciona la mayoría de los middleware_. Con una advertencia en lugar de arrojar un error, el desarrollador de la aplicación puede identificar el problema y refactorizar su propio middleware para solucionarlo.

En su estado actual, todo el middleware existente, incluido el middleware de terceros, que utiliza este patrón de despacho interno (básicamente cualquier cosa que necesite instanciar oyentes en cualquier cosa), está roto.

Lamentablemente, permanecer con 3.x no es una opción para nosotros, ya que usamos TypeScript, y solo 4.x tiene los tipos correctos y, en general, preferimos no quedarnos atascados con una versión heredada de una parte central de nuestro lógica de la aplicación incluso si las mecanografías no fueran un problema.

Y en mi opinión, incluso el mensaje de error en sí mismo insinúa cuán poco importante es realmente: el "problema" es que Other middleware would not be applied to this dispatch , que en realidad rara vez se desea, y hasta ahora, solo ha sido un problema para un caso de uso, donde el middleware de análisis dependía de las acciones enviadas desde un enrutador.

Es importante tener en cuenta que la "corrección" en redux ahora rompe el código que se pretendía arreglar también . Rompe el middleware del enrutador. Ahora los autores de ese middleware (y todos los demás middleware similares) necesitan lanzar versiones compatibles con 4.x de sus bibliotecas, que ahora necesitan exponer los detalles de implementación de una acción "store ready" enviada manualmente, que el consumidor debe implementar.

El uso de bibliotecas de middleware ha cambiado de simplemente importar el paquete e insertarlo en la matriz de middlewares, a ahora, además, se requiere que los autores de la aplicación encuentren alguna forma pirata de enviar una acción arbitraria de "tienda lista" algún tiempo después de que la tienda haya terminado de crearse, pero antes de que se utilice. Y ahora TODO su middleware debe adherirse al mismo tipo de acción, de lo contrario, los autores de aplicaciones necesitarán enviar múltiples acciones "listas para almacenar", con diferentes tipos que coincidan con cualquier API que implementaron los autores de middleware.

Esto es un verdadero lío, y la "cura" es claramente mucho peor que la "enfermedad" en este caso.

Todavía es posible hacer que todos sus ejemplos funcionen. Solo envíe después de aplicarMiddleware. Quizás en algún tipo de función setupMiddleware(store) o en un potenciador de la tienda.

El error se introdujo intencionalmente como alternativa para permitir a los usuarios encontrar un caso extremo que tiene una causa muy poco obvia (a menos que esté familiarizado con los aspectos internos de applyMiddleware ). Redux adopta la postura de prevenir errores del usuario siempre que sea posible, por lo que esto no va a desaparecer.

Una alternativa podría ser algún tipo de devolución de llamada que el middleware pueda registrar como una especie de evento afterApply . Entonces se le garantizará que estará enviando en el momento correcto, después de que todo esté aplicado y en orden. Sería interesante pensar en eso.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

jimbolla picture jimbolla  ·  3Comentarios

CellOcean picture CellOcean  ·  3Comentarios

mickeyreiss-visor picture mickeyreiss-visor  ·  3Comentarios

captbaritone picture captbaritone  ·  3Comentarios

wmertens picture wmertens  ·  4Comentarios