2016-04-17 24 views
8

sto provando a catena più azioni insieme nel modo seguente:concatenamento multiplo invio asincrono in Redux

dati utente A. posta alla base di dati

B. utilizzo pubblicati i dati per interrogare elasticsearch per i risultati

(Faccio A e B in parallelo)

B1. con i risultati di ES, query database originale per i risultati di due tabelle B2. dirige verso nuova pagina e aggiornamento dell'interfaccia utente

sto usando thunk in questo momento per ragionare sul mio codice, ma ho trovato anche questo modello asincrono di essere estremamente verbose:

export function fetchRecipes(request) { 
    return function(dispatch) { 
    dispatch(requestRecipes(request))  
    return fetch(url) 
     .then(response => response.json()) 
     .then(json => dispatch(receiveRecipes(request, json)) 
    ) 
    } 
} 

questo, insieme a "requestRecipes" e "receiveRecipes" come altri creatori di azioni sembra un bel po 'solo per fare una chiamata asincrona. (una richiesta, una ricezione e una funzione di recupero)

sommario: quando si concatenano 2-3 azioni asincrone le cui uscite dipendono l'una dall'altra (ho bisogno di promettere quando possibile), esiste un mezzo più efficiente di farlo senza scrivere 3 funzioni per ogni chiamata asincrona?

Immagino che ci debba essere un modo. Sto cercando di abbinare i documenti di Redux e presto sono stato sopraffatto dalle funzioni che stavo creando

grazie mille per il feedback!

+0

Io uso questo modello; ci sono molte funzioni, ma puoi anche creare una fabbrica che creerà tutte queste funzioni una volta sola all'avvio della tua app. Ad esempio, vedi [questo middleware] (https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/redux/middleware/clientMiddleware.js) nel react-redux-universal -hot-example (Trovo molta ispirazione da vari pattern in quel repository di esempio). Allo stesso tempo, non lo rivendico come l'unico vero modo di usare Redux nel contesto delle azioni asincrone; Sarei curioso di vedere altre risposte qui. – mjohnsonengr

risposta

8

È possibile utilizzare redux-saga anziché redux-thunk per ottenere questo risultato più facilmente. redux-saga ti consente di descrivere il tuo lavoro utilizzando i generatori ed è più facile ragionare.

Il primo passo consiste nel descrivere come si passano i dati in modo riduttivo senza preoccuparsi dei servizi o delle cose asincrone.

azioni

// actions.js 
function createRequestTypes(base) { 
    return { 
    REQUEST: base + "_REQUEST", 
    SUCCESS: base + "_SUCCESS", 
    FAILURE: base + "_FAILURE", 
    } 
} 

// Create lifecycle types on `RECIPES` 
export const RECIPES = createRequestTypes("RECIPES") 

// Create related actions 
export const recipes = { 
    // Notify the intent to fetch recipes 
    request: request => ({type: RECIPES.REQUEST, request}) 
    // Send the response 
    success: response => ({type: RECIPES.SUCCESS, response}) 
    // Send the error 
    error: error => ({type: RECIPES.FAILURE, error}) 
} 

Reducer

// reducer.js 
import * as actions from "./actions" 

// This reducer handles all recipes 
export default (state = [], action) => { 
    switch (action.type) { 
    case actions.RECIPES.SUCCESS: 
     // Replace current state 
     return [...action.response] 

    case actions.RECIPES.FAILURE: 
     // Clear state on error 
     return [] 

    default: 
     return state 
    } 
} 

Servizi

abbiamo bisogno anche l'API ricette. Quando si utilizza redux-saga, il modo più semplice per dichiarare un servizio consiste nel creare una funzione (pura) che legge la richiesta come argomento e restituisce un valore Promise.

// api.js 
const url = "https://YOUR_ENPOINT"; 

export function fetchRecipes(request) { 
    return fetch(url).then(response => response.json()) 
} 

Ora abbiamo bisogno di collegare azioni e servizi. È qui che entra in gioco redux-saga.

// saga.js 
import {call, fork, put, take} from "redux-saga/effects" 
import * as actions from "./actions" 
import * as api from "./api" 

function* watchFetchRecipes() { 
    while (true) { 
    // Wait for `RECIPES.REQUEST` actions and extract the `request` payload 
    const {request} = yield take(actions.RECIPES.REQUEST) 

    try { 
     // Fetch the recipes 
     const recipes = yield call(api.fetchRecipes(request)) 

     // Send a new action to notify the UI 
     yield put(actions.fetchRecipes.success(recipes)) 
    } catch (e) { 
     // Notify the UI that something went wrong 
     yield put(actions.fetchRecipes.error(e)) 
    } 
    } 
} 

function* rootSaga() { 
    yield [ 
    fork(watchFetchRecipes) 
    ] 
} 

E questo è tutto! Ogni volta che un componente invierà un'azione RECIPES.REQUEST, la saga si collegherà e gestirà il flusso di lavoro asincrono.

dispatch(recipes.request(req)) 

Qual è impressionante con redux-saga è che si può facilmente catena di effetti asincroni e le azioni di spedizione durante il flusso di lavoro.

+1

Sto commentando qui perché avevo bisogno di consultarlo di nuovo e la tua spiegazione è incredibile. –

1

In base alla descrizione, l'unica volta che si aggiorna l'interfaccia utente è proprio alla fine di tutte queste operazioni asincrone (B1).

Se non si utilizzano i risultati delle precedenti chiamate asincrone per modificare lo stato dell'applicazione/aggiornare l'interfaccia utente, qual è il vantaggio di avere queste azioni a grana fine?

Ovviamente ci sono cose come "caricamento/richiesta avviata" e "caricamento terminato/richiesta interrotta", ma mi sembra, nel tuo caso, che tu possa semplicemente eseguire le chiamate asincrone concatenate al di fuori del redux (in alcuni tipo di livello API) e utilizzare solo un'azione. Questa azione invia un "REQUEST_STARTED", quindi chiama il livello API, che esegue le richieste DB-calls e elasticsearch, ecc. E quindi invia "REQUEST_SUCCESS" o "REQUEST_FAILURE", in base al risultato della promessa, che fornisci i dati necessari per aggiornare l'interfaccia utente.

In questo modo, lo stato in redux interessa solo se stesso con UN effetto collaterale, anziché i dettagli di implementazione delle chiamate concatenate. Inoltre, la tua azione diventa molto più semplice, perché gestisce solo i risultati di una chiamata asincrona.