The document discusses NgRx and managing state in Angular applications. It introduces NgRx and key concepts like actions, reducers, and effects. It explains how NgRx implements the unidirectional data flow pattern with stores, actions, and reducers. Components dispatch actions and select state from the store as observables. Effects are used to handle asynchronous logic and side effects. Following these patterns with NgRx can help manage complex state and data flow in large Angular applications.
29. @Injectable()
export class AuthService {
private loggedIn = new BehaviorSubject<boolean>(false);
get isLoggedIn() {
return this.loggedIn.asObservable();
}
login(user: User){
if (valid.userName !== '' && user.password != ‘') {
this.loggedIn.next(true);
}
}
logout(){
this.loggedIn.next(false);
}
}
30.
31. O maior problema no desenvolvimento e
manutenção de sistemas de software de
grande escala é a complexidade -
sistemas grandes são difíceis de
entender
Ben Moseley & Peter Marks
Out of the Tar Pit: Analysis of Software Complexity
32. Acreditamos que o principal contribuinte
para esta complexidade em muitos
sistemas é o gerenciamento do estado e
o fardo que isso acrescenta ao tentar
analisar e entender o sistema. Outros
contribuintes estreitamente relacionados
são o volume do código e a preocupação
com o controle de fluxo do sistema.
Ben Moseley & Peter Marks
Out of the Tar Pit: Analysis of Software Complexity
42. Passo 2: Definir estado inicial e reducer
export const counterReducer: ActionReducer<number> = (
state: number = 0,
action: Action
) => {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
case RESET:
return 0;
default:
return state;
}
};
43. Passo 2: Definir estado inicial e reducer
export const counterReducer: ActionReducer<number> = (
state: number = 0,
action: Action
) => {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
case RESET:
return 0;
default:
return state;
}
};
Recebe o estado inicial
44. Passo 2: Definir estado inicial e reducer
export const counterReducer: ActionReducer<number> = (
state: number = 0,
action: Action
) => {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
case RESET:
return 0;
default:
return state;
}
};
Recebe o estado inicial
Recebe a ação + payload
45. Passo 2: Definir estado inicial e reducer
export const counterReducer: ActionReducer<number> = (
state: number = 0,
action: Action
) => {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
case RESET:
return 0;
default:
return state;
}
};
Recebe o estado inicial
Recebe a ação + payload
Retorna novo estado
baseado na ação
49. Flow de dados unidirecional com NgRx
Reducer Component
subscribe
dispatch
Store
Ações
50. import { StoreModule } from '@ngrx/store';
import { counterReducer } from './reducers/counter';
@NgModule({
imports: [
StoreModule.provideStore({ counter: counterReducer })
]
})
export class AppModule { } Reducers Globais
51. Store API
// seleciona todo o estado
this.store.select(state => state);
// seleciona parte do estado
this.store.select(state => state.tasks);
this.store.select('tasks');
// dispara ações
this.store.dispatch({
type: 'ACTION_TYPE',
payload: {...}
});
52. Store API
// seleciona todo o estado
this.store.select(state => state);
// seleciona parte do estado
this.store.select(state => state.tasks);
this.store.select('tasks');
// dispara ações
this.store.dispatch({
type: 'ACTION_TYPE',
payload: {...}
});
53. Store API
// seleciona todo o estado
this.store.select(state => state);
// seleciona parte do estado
this.store.select(state => state.tasks);
this.store.select('tasks');
// dispara ações
this.store.dispatch({
type: 'ACTION_TYPE',
payload: {...}
});
63. Integrando com a Store
@Component({…})
export class CounterComponent {
counter$: Observable<number>;
constructor(private store: Store<number>) {}
ngOnInit() {
this.counter$ = this.store.select<number>('counter');
}
}
64. Integrando com a Store
@Component({…})
export class CounterComponent {
counter$: Observable<number>;
constructor(private store: Store<number>) {}
ngOnInit() {
this.counter$ = this.store.select<number>('counter');
}
}
(1): Declarar Observable
65. Integrando com a Store
@Component({…})
export class CounterComponent {
counter$: Observable<number>;
constructor(private store: Store<number>) {}
ngOnInit() {
this.counter$ = this.store.select<number>('counter');
}
}
(1): Declarar Observable
(2): Injetar Store
66. Integrando com a Store
@Component({…})
export class CounterComponent {
counter$: Observable<number>;
constructor(private store: Store<number>) {}
ngOnInit() {
this.counter$ = this.store.select<number>('counter');
}
} (3): Obter estado relacionado ao Component
87. Effects
Escuta a ação de Pedido e faz dispatch da ação de
“Completo" - que atualiza o estado
@Effect()
loadAction$ = this.actions$
.ofType<task.LoadAction>(task.TaskActions.LOAD)
.map(action => action.payload)
.switchMap(payload =>
this.api
.load()
.map(res => new task.LoadSuccessAction(res))
.catch(error => this.handleError(error))
);
😍
88. Service API
o Serviço da API não sabe do estado nem do redux
@Injectable()
export class TaskService {
private readonly API_TASKS_URL = `http://localhost:3001/tasks`;
constructor(private http: HttpClient) {}
load() {
return this.http.get<Task[]>(this.API_TASKS_URL);
}
create(record: Task) {
return this.http.post<Task>(this.API_TASKS_URL, record);
}
update(record: Task) {
return this.http.put<Task>(`${this.API_TASKS_URL}/${record.id}`, record);
}
remove(id: string) {
return this.http.delete<Task>(`${this.API_TASKS_URL}/${id}`);
}
}
113. Prós e Contras:
1.Fluxo unidirecional ✅
2.Debug volta ao tempo (DevTools) ✅
3.Separação do código ✅
4.Fácil debug e bug fix (1, 2, e 3) ✅
5.Mais fácil pra testar devido à funções puras ✅
6.Melhor performance (onPush) ✅
7.Serialização do estado ✅
8.Mais uma camada == mais código ⛔