Cómo realizar múltiples operaciones/efectos/acciones relacionadas con ngrx / effects


Estoy trabajando en una aplicación que está utilizando ngrx/store 1.5 junto con thunk middleware y estoy intentando pasar a ngrx/store 2.0 y ngrx/effects. Tengo un par de preguntas con respecto a cómo manejar múltiples acciones y/o efectos.

Me doy cuenta de que la "mentalidad" para los thunks vs efectos es diferente y estoy tratando de entender las diferencias. He mirado a través de las aplicaciones de ejemplo disponibles, y no he encontrado nada que parezca encajar con lo que soy intentándolo, así que tal vez todavía me estoy acercando completamente mal.

Hipótesis 1

Aquí hay un efecto secundario para manejar hacer la solicitud al servidor para un inicio de sesión:

@Effect login$: any = this.updates$
    .whenAction(LoginActions.LOGIN)
    .map(toPayload)
    .switchMap(payload => 
        this.loginService.login(payload.user, payload.password)
            .map(result => this.actions.loginSuccess(value))
            .catch((error) => Observable.of(this.loginError(error)))
));

Dado ese efecto secundario inicial, ¿cuál sería la forma "correcta" o "sugerida" de activar la navegación a una pantalla de "inicio" al iniciar sesión correctamente? Esto también podría generalizarse para simplemente desencadenar una secuencia de acciones u operaciones.

Algunas opciones que he considerado:

(a) ¿Otro efecto desencadenado por el éxito del inicio de sesión, que activa una acción posterior para activar la navegación?

@Effect navigateHome$: any = this.updates$
    .whenAction(LoginActions.LOGIN_SUCCEEDED)
    .mapTo(this.actions.navigateHome());

(b) ¿Otro efecto desencadenado por el éxito del inicio de sesión, que simplemente realiza la operación de navegación?

@Effect navigateHome$: any = this.updates$
    .whenAction(LoginActions.LOGIN_SUCCEEDED)
    .do(this.navigateHome())
    .filter(() => false);

(c) Concatenar una acción adicional a las emitidas por el efecto de inicio de sesión inicial? (muestra obviamente no es del todo correcto, pero da la idea)

@Effect login$: any = this.updates$
    .whenAction(LoginActions.LOGIN)
    .map(toPayload)
    .switchMap(password => Observable.concat(
        this.loginService.login(passcode)
            .map(result => this.actions.loginSuccess(value))
            .catch((error) => Observable.of(this.loginError(error))),
        Observable.of(this.actions.navigateHome())
    ));

(d) Other?

Hipótesis 2

Considerar un caso en el que una serie de solicitudes deben ser hecho en secuencia, y a medida que comienza cada solicitud queremos actualizar el "estado" para que se pueda proporcionar retroalimentación al usuario.

Ejemplo de un pensamiento para algo en esas líneas:

multiphaseAction() {
    return (dispatch) => {
        dispatch(this.actions.updateStatus('Executing phase 1');
        this.request1()
            .flatMap(result => {
                dispatch(this.actions.updateStatus('Executing phase 2');
                return this.request2();
            })
            .flatMap(result => {
                dispatch(this.actions.updateStatus('Executing phase 3');
                return this.request3();
            })
            ...
    }
}

Nuevamente, ¿cuál sería la forma "correcta" o "sugerida" de hacer esto al usar el enfoque de efectos?

En este estoy más atascado, no estoy realmente seguro de lo que se podría hacer aparte de agregar algunos .do(this.store.dispatch(this.actions.updateStatus(...)) de alguna manera...

 34
Author: cmatthews.dickson, 2016-06-04

4 answers

La respuesta para el escenario de navegación es su respuesta b

@Effect navigateHome$: any = this.updates$
    .whenAction(LoginActions.LOGIN_SUCCEEDED)
    .do(this.router.navigate('/home'))
    .ignoreElements();

Explicación: Reaccionas al LOGIN_SUCCESS, y debido a que el router no devuelve una nueva acción, necesitamos detener la propagación del flujo, lo que hacemos filtrando todo.

Si se olvida de filtrar, el router devuelve indefinido, lo que a su vez llevará al reductor a reducir un valor indefinido, lo que generalmente resulta en un puntero nulo cuando intenta leer el type del acción

Otra forma de solucionarlo es usar https://github.com/ngrx/router-store

Consulte la documentación sobre cómo agregar router-store a su aplicación.

El mismo efecto ahora se verá así.

import { go } from '@ngrx/router-store';

@Effect navigateHome$: any = this.updates$
    .whenAction(LoginActions.LOGIN_SUCCEEDED)
    .map(() => go(['/home']));

La acción go enviará una acción de enrutador que el reductor de enrutador recogerá y activará un cambio de ruta.

 17
Author: Leon Radley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-14 10:31:06

Hipótesis 1

Considere si navigateHome debe o no cambiar el estado. También, si esta acción navigateHome es enviada o no desde otros lugares para lograr lo mismo. Si es así, devolver una acción es el camino a seguir. Por lo tanto, opción A.

En algunos casos la opción B podría tener más sentido. Si navigateHome solo cambia la ruta, podría valer la pena considerarlo.

Nota al margen: puedes usar ignoreElements aquí en lugar de filter(() => false).

Escenario 2

Sugeriría encadenar sus acciones aquí en múltiples efectos y dar retroalimentación modificando el estado en el reductor para cada acción.

Por ejemplo:

@Effect() trigger$: Observable<Action> = this.updates$
    .whenAction(Actions.TRIGGER)
    .do(() => console.log("start doing something here"))
    .mapTo(Actions.PHASE1);

@Effect() phase1$: Observable<Action> = this.updates$
    .whenAction(Actions.PHASE1)
    .do(() => console.log("phase 1"))
    .mapTo(Actions.PHASE2);

@Effect() phase2$: Observable<Action> = this.updates$
    .whenAction(Actions.PHASE2)
    .do(() => console.log("phase 2"))
    .ignoreElements();

Y dar retroalimentación modificando el estado:

function reducer(state = initialState, action: Action): SomeState {
    switch (action.type) {
        case Actions.TRIGGER: {
            return Object.assign({}, state, {
                triggered: true
            });
        }
        case Actions.PHASE1: {
            return Object.assign({}, state, {
                phase1: true
            });
        }
        case Actions.PHASE2: {
            return Object.assign({}, state, {
                phase1: false,
                phase2: true
            });
        }
        // ...
    }
}
 2
Author: Laurens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-28 09:06:48

Hipótesis 1

La opción A y B son buenas para ir, creo, pero tal vez la opción A es un poco demasiado solo para la navegación! También puede utilizar directamente el router en su LoginActions.LOGIN si considera la navegación como parte de este efecto secundario.

Hipótesis 2

No hay nada de malo en encadenar @Effects. Envía una acción (SET_1_REQUEST) que desencadena tu efecto A. el efecto A devuelve una acción (SET_1_SUCCESS) que es recogida por tu reductor (ahora puedes mostrar ese estado al usuario) y se recoge por efecto B.

Espero que esto tenga sentido.

 0
Author: littleStudent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-21 06:21:46

Estoy aprendiendo estas cosas también.

¿Qué pasa si se envía al reductor cuando el primero en la cadena se hizo?

Algo como esto:

const myReducer = (state,action:Action) => {
switch action.type {
case : "my_first_step" : runfirststep(action.payload);
case : "my_second_step": runsecondstep(action.payload);
....

function runfirststep(data) {
...
//bunch of stuff to do,
//update something to display
store.dispatch('my_second_step');
}

Etc.

El ejemplo ngrx hace esto en el archivo de efectos. Cuando el add o update, llaman UPDATE_COMPLETE o algo similar presumiblemente para que se pueda hacer alguna notificación.

Poco a poco estoy captando lo grande que los reductores terminarán siendo en una aplicación moderadamente compleja.

 -1
Author: Derek Kite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-19 02:04:20