Redux: ¿Cuál es el modismo redux para esperar múltiples llamadas asíncronas?

Creado en 14 sept. 2015  ·  30Comentarios  ·  Fuente: reduxjs/redux

Cuando inicio el componente de reacción, necesito hacer 2 llamadas http asíncronas diferentes y esperar sus resultados.
Idealmente, estas 2 llamadas pondrán sus datos en 2 reductores diferentes.

Entiendo el patrón para realizar una sola llamada asíncrona.
Sin embargo, no veo ninguna buena muestra para hacer múltiples llamadas asíncronas, hacer que se ejecuten en paralelo y esperar a que lleguen sus resultados.

¿Cuál es la forma redux de hacer esto?

question

Comentario más útil

Supongo que usa el middleware Redux Thunk .

Declara algunos creadores de acciones:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

Tenga en cuenta cómo me aseguro de devolver un Promise al final manteniendo la cadena como valor de retorno de la función dentro de los creadores de acciones de procesador. Esto me permite hacer eso:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

Cuando usa Redux Thunk, dispatch devuelve el valor de retorno de la función que devolvió del creador de la acción del procesador, en este caso, la Promesa.

Esto le permite usar combinadores como Promise.all() para esperar a que se completen varias acciones:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

Sin embargo, es preferible definir otro creador de acciones que actuaría como una _composición_ de los creadores de acciones anteriores y usaría Promise.all internamente:

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

Ahora solo puedes escribir

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

¡Feliz envío!

Todos 30 comentarios

Supongo que usa el middleware Redux Thunk .

Declara algunos creadores de acciones:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

Tenga en cuenta cómo me aseguro de devolver un Promise al final manteniendo la cadena como valor de retorno de la función dentro de los creadores de acciones de procesador. Esto me permite hacer eso:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

Cuando usa Redux Thunk, dispatch devuelve el valor de retorno de la función que devolvió del creador de la acción del procesador, en este caso, la Promesa.

Esto le permite usar combinadores como Promise.all() para esperar a que se completen varias acciones:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

Sin embargo, es preferible definir otro creador de acciones que actuaría como una _composición_ de los creadores de acciones anteriores y usaría Promise.all internamente:

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

Ahora solo puedes escribir

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

¡Feliz envío!

@gaearon ¿Hay alguna razón en particular por la que thunk sobre promise middleware? o consejos sobre cuándo usar qué? Gracias.

Sin ninguna razón en particular, solo estoy más familiarizado con él.
¡Pruebe ambos y use lo que funcione mejor para usted!

¡Entendido, gracias!

JFYI :)
screen shot 2015-09-14 at 11 06 42 am

si quieres puedes echarle un vistazo https://github.com/Cron-J/redux-async-transitions

@gaearon pregunta rápida: mirando su ejemplo anterior, donde llama:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

- Parece que el bloque "Hice algo" se activará incluso si doSomething rechazado, ya que no volverá a lanzar el error en la cadena doSomething después de detectarlo. ¿Es eso intencional? Si no es así, y si en su lugar volviera a lanzar el error allí, ¿lo manejaría nuevamente en el llamador?

Es solo un ejemplo, no me refiero a que sea una referencia definitiva, solo como una forma de verlo. No le di un minuto de pensamiento real.

Sabes cómo devolver las promesas de los creadores de acciones, el resto depende de ti. Ya sea para volver a lanzar los errores, dónde manejarlos, depende de usted. Esto no es realmente una preocupación de Redux, por lo que mi consejo es tan bueno como el de cualquier otra persona, y en este caso tienes mucho más contexto sobre lo que quieres construir y el estilo que prefieres del que yo tendré.

¡Genial, gracias! Pensé que ese era el caso, pero quería asegurarme de que no hubiera algo de magia que estuviera pasando por alto.

Dado que Redux es personalizable, no se garantiza que el valor de retorno de dispatch sea ​​una promesa esperada.

En realidad, me encontré con un problema en la vida real ya que tomo redux-loop. Modifica dispatch para devolver una Promesa envuelta (https://github.com/raisemarketplace/redux-loop/blob/master/modules/effects.js#L38). Por lo tanto, no podemos depender del valor de retorno de dispatch .

Bueno, como dijo Dan anteriormente en este hilo: es _ tu_ aplicación, _tú_ estás escribiendo a tus creadores de acciones, así que _tú_ puedes decidir qué devuelven.

@markerikson No se dispatch sea ​​el valor de retorno del creador de la acción, ya que el middleware puede modificarlo.

Pero tengo una solución. En lugar de encadenar then en dispatch(actonCreator()) , podemos encadenar en actionCreator() .

Sí, el middleware puede modificar las cosas, pero mi punto es que _tú_ estás configurando el middleware en tu propia aplicación :)

Hola @gaearon , ¡

function fetchDataBWhichDependsOnA() {
  return dispatch => {
    dispatch(fetchDataA())
      .then(() => {
        fetch('/api/get/data/B')
          .then(dispatch(receiveDataB(response.data)))
      })
  }
}

Hay 2 acciones asíncronas anidadas en este fragmento de código, funcionan pero tengo problemas para probarlo. Puedo probar fetchDataA() porque solo tiene 1 nivel de llamada asincrónica. Pero cuando estaba intentando escribir una prueba para fetchDataBWhichDependsOnA() , no pude recuperar los valores más actualizados del bloque store en then .

@timothytong No estás devolviendo las promesas. Si devuelve el resultado de dispatch y la llamada fetch anidada, debería poder esperar en sus pruebas hasta que se haya resuelto toda la cadena.

@johanneslumpe buena captura, gracias amigo! Ahora todo lo que queda es la pregunta de si esto es aceptable o no, ¿lidiar con la dependencia de datos con una cadena de búsqueda asíncrona?

Tuve el mismo problema y pasé 30 minutos tratando de encontrar una solución. No he terminado esta implementación, pero siento que está en el camino correcto.

https://github.com/Sailias/sync-thunk

Mi idea es que un componente puede solicitar que se completen ciertos estados redux, si no lo están, debe consultar un mapa para llamar a las acciones para completarlos y encadenarlos con then . Cada acción en la cadena no necesita que se pasen datos, solo puede tomar los datos del estado tal como lo habrían poblado las acciones dependientes.

@gaearon

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

Tengo una pregunta sobre esto. Lo siento si es tan básico.

En este escenario, habrá dos despachos. Eso significa que se procesará el componente dos veces. Pero, ¿qué pasa si el componente quiere que ambos datos muestren algo significativo?

Entonces, en ese escenario, me gustaría que el componente se renderice solo una vez y es entonces cuando se completan todas las acciones asíncronas.

Obtuve nombres de algunos middlewares que parecen estar manejando este problema, como redux-batched-subscribe y redux-batched-actions . Pero, un poco confundido sobre el mejor enfoque.

Si este es un escenario válido, ¿cuál sería el enfoque sugerido para manejar esto?

@jintoppy si su pregunta es evitar múltiples llamadas para renderizar un componente, entonces la solución Promise.all no le ayudará; porque ambos doSomething() and doSomething() todos los datos de envío que desencadenarían un cambio de estado y provocarían la representación del componente.

@jintoppy, ¿por qué necesita algún middlwares para manejar su situación?
¿Qué te impide hacer algo así?

// action creator
function fetchAB() {
  return dispatch => {
    const fetchA = fetch( 'api/endpoint/A' );
    const fetchB = fetch( 'api/endpoint/B' );
    return Promise.all([ fetchA, fetchB ])
      .then( values => dispatch(showABAction(values)) )
      .catch( err => throw err );
  }
} 

Puede poner toda su solicitud en el creador de una acción y disparar el envío de la acción solo una vez que se hayan resuelto todas. En ese caso, ha llamado a reducer y, por lo tanto, el estado cambió solo una vez, por lo que es probable que render () también se active solo una vez.

@apranovich : Veo un problema con este enfoque. Ahora, necesito combinar la recuperación múltiple en una sola acción. Entonces, si tengo, digamos 10 llamadas asíncronas, tengo que poner todas esas en una sola acción. Además, ¿qué sucede si quiero activar una de estas 10 llamadas asíncronas más adelante? En este enfoque, básicamente, toda su lógica asincrónica estará en una sola acción.

Gracias a todos por la información útil. Pregunta rápida, ¿cómo se puede lograr lo mismo pero, en cambio, cuando una búsqueda devuelve los datos que requiere el siguiente método de búsqueda? Por ejemplo, hay un punto final de API meteorológico gratuito donde ingreso la geolocalización para obtener una identificación (primer fetch ) que se requiere para el próximo fetch para obtener el clima de la ubicación. Sé que este es un caso de uso extraño, pero me gustaría saber cómo manejar situaciones similares. ¡Gracias por adelantado!

Hola @gaearon , me gustaría recibir tu opinión sobre esta pregunta mía. Tengo que hacer llamadas asíncronas anidadas a dos API. La respuesta de la segunda API depende de la respuesta de la primera.

export const getApi=()=>{
        return(d)=>{
        axios({
            method:'GET',
            url:Constants.URLConst+"/UserProfile",
            headers:Constants.headers
        }).then((response)=>{
            return d({
                type:GET_API_DATA,
                response,
                axios({
                    method:'GET',
                    url:Constants.URLConst+"/UserProfileImage?enterpriseId="+response.data.ProfileData.EnterpriseId,
                    headers:Constants.headers
                }).then((response1)=>{
                    return d({
                        type:GET_PROFILE_IMAGE,
                        response1
                    })
                })
            })
        }).catch((e)=>{
            console.log("e",e);
        })
    }

}

Intenté algo como el anterior, pero esto no funciona Y en mi proyecto, tengo que usar la respuesta de ambas API de forma independiente.
Entonces, ¿cuál es la forma correcta de hacerlo?

Gracias.

@ c0b41 , verifique la pregunta actualizada.

hey @ c0b41 , esto está dando errores de sintaxis en la segunda llamada de axios.

@ aayushis12 , @ c0b41 : este es un rastreador de errores, no un foro de ayuda. Debería intentar hacer esta pregunta en Stack Overflow o Reactiflux. Habrá más personas que verán la pregunta y podrán ayudar.

Para aquellos que experimentan eso

yo hice todo

se imprime antes de que se resuelvan todas las llamadas asíncronas, compruebe si está devolviendo una declaración o una expresión de sus creadores de acciones asíncronas.

Por ejemplo, si doSomethingElse() se declara con {} en la función de flecha

function doSomethingElse() {
  return dispatch => {
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
  }
}

"Hice todo" se imprimirá antes de que doSomethingElse() resuelva. Resuelva esto soltando {} después de => .

@gaearon una pregunta, basada en https://github.com/reduxjs/redux/issues/723#issuecomment -139927639 ¿cómo lo manejaría si necesita enviar una acción que espera a que se hayan enviado dos acciones _Y_ el estado? haber cambiado debido a esas acciones? Tengo un caso en el que las acciones se envían pero la acción dentro de then se ejecuta _antes_ que el estado haya cambiado. Estamos usando redux-thunk .

@enmanuelduran : Dan ya no es un mantenedor activo, no le

Es difícil responder a su pregunta sin ver el código, pero nuestros problemas realmente no pretenden ser un foro de discusión. Sería mejor si publicas tu pregunta en Stack Overflow; es probable que obtengas una mejor respuesta allí, más rápido.

@markerikson oh, lo siento, no era mi intención, creé una pregunta en stackoverflow si alguien está interesado: https://stackoverflow.com/questions/51846612/dispatch-an-action-after-other-specific-actions-were- enviado-y-cambio-de-estado

Muchas gracias chicos.

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