2015-12-09 10 views
11

C'è un modo per annullare un'azione o ignorarla?Come annullare/ignorare un'azione in redusso

O meglio, qual è il modo migliore/consigliato per ignorare un'azione?

Ho la seguente creatore azione e quando ho ingresso una dimensione non valida (diciamo 'some_string') nel creatore di azione, oltre a ottenere il mio messaggio di avviso Ho anche: Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.

import { SET_SELECTED_PHOTOS_SIZE } from './_reducers'; 

export default (size=0) => { 
    if (!isNaN(parseFloat(size))) { 
    return { 
     type: SET_SELECTED_PHOTOS_SIZE, 
     size: size, 
    }; 
    } else { 
    app.warn('Size is not defined or not a number'); 
    } 
}; 

ho discusso nel redux -channel in Discord (reactiflux) dove uno stato suggerito di usare redux-thunk simili:

export default size => dispatch => { 
    if (!isNaN(parseFloat(size))) { 
    dispatch({ 
     type: SET_SELECTED_PHOTOS_SIZE, 
     size: size, 
    }); 
    } else { 
    app.warn('Size is not defined or not a number'); 
    } 
} 

l'altra opzione era di ignorare l'azione all'interno del riduttore . Questo rende il riduttore "più grasso" perché ha quindi più responsabilità, ma utilizza meno azioni thunk che rende più facile il debug. Potevo vedere il modello di thunk sfuggire di mano dato che sarei stato costretto a usarlo per quasi ogni azione, rendendo le azioni in batch un po 'difficili da mantenere se ne hai molte.

risposta

9

Ignorare le azioni in Action Creators è fondamentalmente un modo di trattarle come Command Handler, non come Event Creators. Se l'utente fa clic sul pulsante, si tratta comunque di una sorta di evento.

Quindi ci sono fondamentalmente due modi per risolvere il problema:

  1. La condizione è all'interno creatore azione e thunk-middleware viene utilizzato

    const cancelEdit =() => (dispatch, getState) => { 
        if (!getState().isSaving) { 
        dispatch({type: CANCEL_EDIT}); 
        } 
    } 
    
  2. La condizione si trova all'interno del riduttore e non middleware è richiesto

    function reducer(appState, action) { 
        switch(action.type) { 
        case: CANCEL_EDIT: 
        if (!appState.isSaving) { 
         return {...appState, editingRecord: null } 
        } else { 
         return appState; 
        } 
        default: 
        return appState; 
    
        } 
    } 
    

preferisco fortemente trattare l'interazione UI come eventi al posto di comandi e ci sono due vantaggi:

  1. Tutta la vostra logica di dominio rimane in riduttori puri sincroni che sono molto facili da testare. Immagina di dover scrivere un test unitario per la funzionalità.

    const state = { 
        isSaving: true, 
        editingRecord: 'FOO' 
    }; 
    
    // State is not changed because Saving is in progress 
    assert.deepEqual(
        reducer(state, {type: 'CANCEL_EDIT'}), 
        state 
    ); 
    
    // State has been changed because Saving is not in progress anymore 
    assert.deepEqual(
        reducer({...state, isSaving: false}), 
        {isSaving: false, editingRecord: null} 
    ); 
    

Come si può vedere il test è davvero semplicemente quando si trattano l'interazione come Evento

  1. Che cosa succede se si decide che invece di ignorare l'azione preferiresti mostrare qualche indicazione visiva che l'azione non è possibile? Dovresti inviare un'altra azione o sostanzialmente ricostruirla. Tuttavia, non è possibile utilizzare hot-reload con replay qui perché la logica in action creator non è riproducibile. Se la logica è in riduttore, puoi semplicemente cambiare il comportamento, il riduttore verrà ricaricato a caldo e tutti gli eventi verranno riprodotti. L'unico evento che invii è che l'utente ha fatto clic su un pulsante e non puoi negarlo. Quindi, a meno che non cambi drasticamente l'interfaccia utente, puoi sempre ricaricare a caldo con la riproduzione.

Quando si pensa a qualsiasi interazione con l'interfaccia utente come un evento, allora si otterrà la migliore esperienza di riproduzione possibile, perché gli eventi non può essere negato che hanno appena accaduto.

+0

Per quanto riguarda l'aspetto di ricarica a caldo, se si ripetono le azioni così come vengono ricevute dal riduttore, allora si dovrebbe finire con lo stesso stato in entrambi i casi, e se altre azioni vengono attivate nel creatore, la riproduzione di quelle dovrebbe comunque risultare nel stesso stato, supponendo che il riduttore sia puro – Moshe

+0

Questo è vero, ma come ho spiegato nell'esempio sopra. Nel primo approccio (usando thunk-middleware) l'azione non viene nemmeno inviata e quindi l'informazione viene persa e l'informazione è che l'utente ha fatto clic sul pulsante. Questo è un esempio banale ma nel mondo reale si potrebbe finire con una logica molto pesante nei creatori di azioni che si traduce in molte Azioni (Eventi) ma la logica per generare le azioni non è comunque riproducibile. Tuttavia, non è possibile negare l'interazione con l'interfaccia utente (facendo clic sul pulsante). –

+0

Per ignorare le azioni in redux, dovresti dare un'occhiata a [redux-ignore] (https://github.com/omnidan/redux-ignore). È stato raccomandato da Dam Abramov stesso. – umphy