4. In Angular, React and most of modern javascript frameworks/libs
EVERYTHING IS A COMPONENT
4 / 89
5. IN THE REAL WORLD :(
UI is not divided in several components
but most of the time there is only a main (big) component for each view
routes/views are often a component with a long html template
and contain a lot of business logic (JS) into it
Components are often stateful and/or there is an abuse of dependency injection
Especially in Angular
5 / 89
8. WHAT ARE THE PROBLEMS?
If application grows, the big picture is lost
Code is unpredictable.
Data flow is hard to follow => Mistakes and bugs are very di cult to find
App does not scale and it's not easy to mantain, to debug and to test.
If developers leave the project it can became a nightmare since projects are often
not so well-documented and nobody else really know how it works
8 / 89
9. A SOLUTION?
Apply properly Angular/React best practices, patterns, styleguides and
use a state management pattern, such as Redux
9 / 89
10. The first concept to understand is how to
organize your app in stateless components
10 / 89
11. COMPONENTS in ANGULAR (2+)
...
<menu [items]="users" (onItemClick)="active = $event"></menu>
<gmap [lat]="active.lat" [lng]="active.lng"></gmap>
<chart [title]="active.name" [config]="active.data"></chart>
...
What's happen here? When a menu item is selected, the google map and chart components are
populated with new data. Each component is responsable of its own behaviors and data flow should
be handled by a parent
11 / 89
20. WHY IN COMPONENTS?
Each component manages a small part of the view
Separation of concerns
Reusable
Composable
Testability
Semantic HTML: readable and simple to mantain
NOTE: components should be stateless:
just display stu and don't care about data flow
20 / 89
21. So, WHAT'S THE GLUE?
How components can be updated,
can communicate or stay in sync with each others ?
21 / 89
22. DATA ARCHITECTURES BEFORE REDUX
Frequently solutions to handle data before Redux:
1. Events => spaghetti code. Hard to manage : P
2. Dependency Injection => unpredictable :(
3. Manage state in a stateful parent component => one of the best solutions so far : )
22 / 89
28. Redux is the only one to know data and can modify it
28 / 89
29. HOW?
1) Avoid keeping application state in UI component itself
2) Instead we can handle it in a Redux store
State is easy to handle and it's protected by the immutability guarantees of the
Redux architecture. Dan Abramov, author of Redux
29 / 89
30. WHAT IS
A library that helps you manage the state
of your (large) applications
30 / 89
31. WHEN IS REDUX USEFUL?
SPA with complex data flow (and sophisticated UI)
Multiple views/routes that share data
Multiple components that must be in sync (with no parent relationship)
Scalability
Testability
Simplify debug
Consistently: run in di erent environments (client, server, and native)
31 / 89
33. MAIN BENEFITS
Decoupled architecture from UI
Predictable application state. One source of truth
Immutable state tree which cannot be changed directly
Components cannot mutate data. Less mistakes
Maintanibility
Organization
Scale 33 / 89
34. DEBUG BENEFITS
Easy to test: no mocks, spies or stubs
Redux DevTools
Track state (and di )
Track actions
Skip / Jump to actions
Time Travel Debug
Export / Import store
...
34 / 89
35. OTHER BENEFITS
Community
Ecosystem: a lot of Plugins & Enhancers
Persist / LocalStorage, Router, Forms, Middlewares, Log, Undo/Redo, ...
Can be shared across frameworks / libraries
React, Angular, Vue, Polymer, vanillaJS, ...
It can be used client, server (SSR) and mobile
It's (like) a pattern and used all over the world 35 / 89
36. But the real first thing to know about Redux is...
36 / 89
38. This architecture might seem like an overkill,
but the beauty of this pattern
is how well it scales to large and complex apps.
Dan Abramov - Author of Redux
38 / 89
40. THREE PRINCIPLES OF REDUX
1. Single source of truth: Store
2. State is read-only
3. Changes are made with pure functions
Source: Redux documentation
40 / 89
41. STORE, ACTIONS, REDUCERS: KEY-POINTS
1. The whole state of your app is stored in an object tree inside a single store.
2. The only way to change the state tree is to emit an action.
3. To specify how the actions transform the state tree, you write pure reducers.
41 / 89
46. 1. STORE / STATE
A single JS object that contains the current state of the application:
{
configuration: { theme: 'dark' }
auth: { token: 'abc123' }
users: {
list: [{}, {}, {}],
selected: { id: 123}
}
}
Store represents a kind of local db of your data (but it's not saved anywhere. It is in memory)
IMPORTANT: only actions can mutate the state, by using a reducer
46 / 89
48. WHY I LOVE HAVING A SINGLE STORE? An example:
1. You (or your customer) discover a bug!
2. Store can be exported (in a JSON)
3. Store can be imported to re-create the scenario that has generated the bug
Your app must be completely stateless in order to apply this workflow
HOW? By using Redux Dev Tools. See next slides
48 / 89
49. 2. ACTIONS
Plain JS objects that represents somethings that has happened in the application. Similar to events.
{
type: SET_CONFIG, // action type
payload: { theme: 'light' } // action payload (data)
}
{
type: USER_DELETE,
payload: 1001
}
{
type: USER_ADD,
payload: { name: 'Mario', email: ... }
}
49 / 89
50. You can track, jump or skip any action using REDUX Chrome DevTools
50 / 89
53. ACTION CREATORS: functions that create actions
actions/counterActions.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const increment = () => {
const action = { type: INCREMENT }; // action type
dispatch(action); // `dispatch` is provided by redux
};
export const decrement = () => {
const action = { type: DECREMENT }
dispatch(action);
};
increment and decrement methods are invoked by the UI
53 / 89
54. COMPONENTS invoke action creators
components/counter.component.js|ts
<!-- angular -->
<button (click)="actions.increment()"> add </button>
In Angular, actions can be injected by using a service / provider
<!-- react -->
<button onClick={ () => props.increment() }> add </button>
In React, actions are passed as props by a parent component (knows as container)
54 / 89
55. 3. REDUCERS
A function that specify how the state changes in response to an action.
(like an event handler that determine how state must be changed)
55 / 89
56. A reducer never modifies the state...
// wrong!
myArray.push('anything');
myObj.property = 'anything';
56 / 89
57. ... but it returns an (updated) copy of the state
// merge by using Lodash / Underscore => 3rd party lib
return _.merge(state, payload)
// clone current state and merge payload => ES5
return Object.assign({}, state, payload);
// same as previous => object spread operator (ES2018)
return { ...state, ...payload };
// clone array and add element => array spread operator (ES2015)
return [ ...users, payload ];
The important concept is avoid mutating the store
57 / 89
58. WHY WE CANNOT MUTATE THE ORIGINAL OBJECT?
1. After each action, REDUX expects a brand new object from the reducer, if there are state updates
2. Redux compares the new object with the previous one, checking the memory location
The comparison is done by using !==. A deep-compare match would be more expensive so the best
way to compare 2 object is by using the memory location
So, this comparison would not be possible if you directly modify the original state
Read a great article about this topic
58 / 89
60. What's an inpure function?
Any function that doesn’t alter data and doesn’t depend on external state (like DB, DOM, or global variables)
function increment(obj) {
obj.count++; // NO mutate arguments
}
function increment(user) {
service.doSomething(user); // NO side effects
}
function increment(input) {
obj.count = Math.random(); // NO random, new Date(), ...
}
60 / 89
61. Pure functions
Instead of the following:
function increment(state) {
state.count++; // NO! mutate the state
}
A pure function should return a new copy of the object:
function increment(state) {
return {
count: state.count + 1 // OK! return a new state
};
}
61 / 89
74. 5A. VIEW: get State
// views/dashboard.component.ts
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from '../model/user';
@Component({
selector: 'dashboard',
template: '<list [data]="myUsers"></list>'
})
export class DashboardComponent {
@select('users') public users$: Observable<User[]>; // select state
myUsers: User[];
constructor() {
this.users$.subscribe(res => this.myUsers = res);
}
} 74 / 89
75. 5B. VIEW: get State and async pipe
// views/dashboard.component.ts
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from '../model/user';
@Component({
selector: 'dashboard',
template: '<list [data]="myUsers | async"></list>'
})
export class DashboardComponent {
@select('users') public users$: Observable<User[]>; // select
}
75 / 89
76. 5C. VIEW: get state and invoke actions
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from '../model/user';
import { UsersActions } from '../actions/users.actions';
@Component({
selector: 'dashboard',
template: `<list [data]="users$ | async"
(delete)="actions.deleteUser($event)"></list>`
})
export class DashboardComponent {
@select('users') public users$: Observable<User[]>; // select
constructor(public actions: UsersActions) {
actions.getUsers();
}
}
76 / 89
77. Do NOT you like to angular-redux/store?
Try ngrx. It's awesome!
ngrx is not a Redux binding (as Angular Redux/Store), it has been written from scratch and it widely
use RxJS. It also includes middlewares, memoization, lazy-store/e ects and a lot of other cool stu
out of the box.
Anyway Angular Redux/Store is easier to learn.
77 / 89
83. Main Application Component
// index.js
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from ''./reducers';
import App from './App';
const store = createStore(rootReducer);
render(<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
WHAT WE DID: initialize Redux Store
83 / 89
85. Dashboard: Container
Connect Redux to components
// views/DashboardContainer.jsx
import * as React from 'react';
import { connect } from 'react-redux';
import { addUser, deleteUser } from "../../actions/usersActions";
import { DashboardView } from './view/dashboard';
const DashboardContainer = connect(
state => ({ users: state.users }),
{ addUser, deleteUser }
)(DashboardView);
export default DashboardContainer;
WHAT WE DID: we sent state and actions to the DashboardView component as props 85 / 89
86. DashboardView: Presentational component
Get state from redux (by props) and invoke actions
// views/DashboardView.jsx
export const DashboardView = props => (
<div>
<h1>{props.users.length} members</h1>
<AddForm submit={user => props.addUser(user)}/>
<Users {{...props}} onDelete={id => props.deleteUser(id)} />
</div>
);
Presentational components should know nothing about Redux,
so you can reuse them without it as well.
86 / 89
87. And now? There's still a lot of stu to learn
Read the Redux Documentation
Watch "Getting Started with Redux" videos by Dan Abramov
Check the Redux binding for your favorite library
Join my training courses: fabiobiondi.io ;)
87 / 89