2016-02-15 12 views
12

Wyobraź sobie sytuację obsługi formularza, który może zwracać różne błędy: 400, 401, 500. Po zwróceniu 400, chcę wyświetlić komunikat na górze formularza (zastąp domyślne zachowanie). W przypadku innych (nieobsługiwanych) kodów błędów należy uruchomić domyślną (globalną) procedurę obsługi błędów (która wyświetla powiadomienie toast). Po prostu nie chcą powielać ten kod dla każdego działaniaJak utworzyć globalną procedurę obsługi błędów w Redux i przesłonić ją w razie potrzeby?

I wysyłką async akcje używając 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); 

mogę utworzyć reduktor, który obsługuje wszystkie {Błąd: true} działań i pokaż niektóre wyskakujące powiadomienia (prawdopodobnie bez użycia stanu redux, bezpośrednio wywołując metodę toast.show()). Ale jak ustalić, czy ten specjalny błąd był już obsługiwany przez jakiś inny reduktor?

Odpowiedz

19

Zanim działanie dotrze do reduktora, jest to fakt. Odzwierciedla to, co już się stało. Nie ma sensu pytać "czy inny reduktor sobie z tym poradził?", Ponieważ reduktory mają być pasywne i, w ogólnym sensie, nieświadome istnienia siebie nawzajem. Powinni starać się być niezależni, jeśli to możliwe.

Nie ma jednego "prawdziwego" sposobu na osiągnięcie tego, co chciałeś, ale ponieważ już używasz konwencji traktowania dowolnego obiektu z właściwością error jako błędu globalnego, możesz równie dobrze wprowadzić inną konwencję, na przykład "jeśli akcja ma flagę suppressGlobalErrorNotification, wtedy globalny reduktor błędu nie powinien się tym przejmować ".

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

Popraw mnie jeśli się mylę @DanAbramov, ale w funkcji redukującej "błąd", czy nie musi najpierw sprawdzać 'RESET_ERROR'? Po prostu próbowałem tego i kiedy próbujesz zresetować komunikat o błędzie nic się nie dzieje. Myślę, że dzieje się tak dlatego, że pierwszy warunek "if" jest prawdziwy, ponieważ 'action.error' jest oczywiście niezdefiniowany podczas resetowania komunikatu o błędzie. – Jonathan

-1

To samo dla mnie, nie znalazłem żadnego działającego rozwiązania, niż sobie z tym poradzę. @Dan Abramov pokazał przykład, ale problem tutaj, kiedy masz dziesiątki formularzy, staje się bardziej skomplikowana. Za każdym razem, gdy potrzebujesz obsługiwać te same rzeczy, duplikowanie kodu staje się denerwujące. Na przykład:

form => client validation => CLIENT_VALIDATION_ERROR => reducer 

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

Nie może być wyjątkiem, gdzie musimy ręcznie, ale w większości przypadków obsługiwać takie zachowanie nie jest.

+1

Jeśli masz zduplikowany kod, wyodrębnij go w funkcji. –

+0

To prawda, ale powinien istnieć jakiś rodzaj generatora po stronie akcji i reduktora, aby pominąć wszystkie ciągłe tworzenie i obsługę aux. Ukrywa prawdziwą implementację od programisty i staje się magią (niektórzy programiści mogą tego nie lubić). –

+0

Trudno dyskutować bez konkretnego przykładu. Nie rozumiem, w jaki sposób podejście, które zasugerowałem powyżej, prowadzi jeszcze do powielenia. Czy możesz pokazać sens? –

Powiązane problemy