2016-02-15 4 views
12

Immaginare una situazione di gestione del modulo di invio che può restituire diversi errori: 400, 401, 500. Quando viene restituito 400, voglio mostrare un messaggio nella parte superiore del modulo (sovrascrivere il comportamento predefinito). Per altri codici di errore (non gestiti), deve essere richiamato il gestore di errori (globale) predefinito (che mostra il brindisi di notifica). Solo che non voglio duplicare questo codice per ogni singola azioneCome creare un gestore di errori globale in Redux e sovrascriverlo quando necessario?

ho spedizione azioni asincrone utilizzando Redux-thunk middleware

// Pseudo code 
const action = (dispatch) => { 

    const onSuccess = (result) => dispatch({type: 'OPERATION_SUCCESS', payload: result}); 
    const onError = (error) => dispatch({type: 'OPERATION_ERROR', error: true, payload: error}); 

    return promise.then(onSuccess, onError); 

}; 
dispatch(action); 

posso creare un riduttore che gestisce tutti {errore: true} azioni e mostra alcune notifiche popup (probabilmente senza utilizzare lo stato di Redux, invocando direttamente qualche metodo toast.show()) Ma come determinare se questo errore speciale è già stato gestito da qualche altro riduttore?

risposta

19

Nel momento in cui un'azione raggiunge un riduttore, è un . Riflette qualcosa che è già successo. Non ha senso chiedere "l'altro riduttore ha gestito questa azione?", Perché i riduttori dovrebbero essere passivi e, in generale, inconsapevoli dell'esistenza reciproca. Dovrebbero sforzarsi di essere indipendenti, ove possibile.

Non esiste un modo "vero" per realizzare ciò che si desidera, ma poiché si utilizza già la convenzione di trattare qualsiasi oggetto con una proprietà error come un errore globale, si potrebbe anche introdurre un'altra convenzione come "se l'azione ha un flag suppressGlobalErrorNotification quindi il riduttore di errori globale non dovrebbe preoccuparsene ".

// utilities 

function checkStatus(response) { 
    if (response.status >= 200 && response.status < 300) { 
    return response 
    } else { 
    const error = new Error(response.statusText) 
    error.response = response 
    throw error 
    } 
} 

function parseJSON(response) { 
    return response.json() 
} 

export function post(url, data) { 
    const options = { 
    method: 'POST', 
    headers: { 
     'Accept': 'application/json', 
     'Content-Type': 'application/json' 
    }, 
    body: JSON.stringify(data) 
    } 
    return fetch(url, options) 
    .then(checkStatus) 
    .then(parseJSON) 
} 

// action creators 

import { post } from './utils' 

export function submitForm(data) { 
    return dispatch => post('/myform', data).then(
    response => dispatch({ 
     type: 'SUBMIT_FORM_SUCCESS', 
     payload: response 
    }), 
    error => dispatch({ 
     type: 'SUBMIT_FORM_FAILURE', 
     error: error, 
     suppressGlobalErrorNotification: (
     error.response && 
     error.response.status === 400 
    ) 
    }) 
) 
} 

// reducers 

export function error(state = null, action) { 
    if (!action.error || action.suppressGlobalErrorNotification) { 
    return state 
    } 
    if (action.type === 'RESET_ERROR') { 
    return null 
    } 
    return action.error 
} 


export function form(state = {}, action) { 
    switch (action.type) { 
    case 'SUBMIT_FORM_FAILURE': 
    return Object.assign({}, state, { isFormError: true }) 
    // ... 
    default: 
    return state 
    } 
} 
+0

Correggetemi se ho torto @DanAbramov, ma nella funzione di riduttore 'error', non è necessario prima controllare 'RESET_ERROR'? Ho appena provato questo e quando si tenta di ripristinare il messaggio di errore non succede nulla. Penso che questo sia dovuto al fatto che la prima condizione 'if' è vera perché' action.error' è chiaramente indefinito quando si resetta il messaggio di errore. – Jonathan

-1

Stesse cose per me, non ho trovato alcuna soluzione di lavoro che in grado di gestirlo. @Dan Abramov ha mostrato l'esempio ma il problema qui è che quando si hanno decine di moduli le cose diventano più complicate. Ogni volta che devi gestire le stesse cose, il codice duplicato inizia a essere fastidioso. Per esempio:

form => client validation => CLIENT_VALIDATION_ERROR => reducer 

         FETCH_STARTED 
form => form submit => SERVER_SIDE_SUCCESS => reducers 
         SERVER_SIDE_ERROR 

Non ci può essere un'eccezione in cui abbiamo bisogno per gestire tale comportamento manualmente ma nella maggior parte dei casi non lo è.

+1

Se si dispone di codice duplicato, estrarlo in una funzione. –

+0

Vero, ma dovrebbe esserci qualche tipo di generatore sul lato dell'azione e del riduttore, per saltare tutte le operazioni di creazione e gestione delle costanti ausiliarie. Nasconde la vera implementazione dallo sviluppatore e diventa una magia (alcuni sviluppatori potrebbero non gradirlo) –

+0

È difficile discutere senza alcun esempio specifico. Non vedo come l'approccio che ho suggerito sopra porti ancora a una duplicazione. Puoi mostrare un succo? –