Using Redux

and Angular 2

 by Gerard Sans (@gerardsans)

Google Developer Expert

Master of Ceremonies

International Speaker

Angular 2 Trainer

Community Leader

800

500

Blogger

Redux

Dan Abramov

@gaearon

Main Features

  • Unidirectional data flow
  • Single Immutable State
  • New states are created without side-effects

Unidirectional data flow

Single Immutable State

  • Helps tracking changes by reference
  • Improved Performance
  • Enforce by convention or using  a library. Eg: Immutable.js

Immutable by Convention

  • New array using Array Methods
    • map, filter, slice, concat
    • Spread operator (ES6) [...arr]
  • New object using Object.assign (ES6)

immutable List and Map

let selectedUsers = Immutable.List([1, 2, 3]);  
let user = Immutable.Map({ id: 4, username: 'Spiderman'}):

let newSelection = selectedUsers.push(4, 5, 6); // [1, 2, 3, 4, 5, 6];  
let newUser = user.set('admin', true);  
newUser.get('admin') // true

immutable Record

var ABRecord = Immutable.Record({a:1, b:2});
var myRecord = new ABRecord({b:3});

myRecord.get('b') // 3
myRecordWithoutB = myRecord.remove('b')
myRecordWithoutB.get('b') // 2

myRecord.b // 3
myRecord.b = 5 // throws Error

myRecord.set('b', 5) 
myRecord.b // 5

Reducers

  • Reducers create new states in response to Actions applied to the current State
  • Reducers are pure functions
  • Don't produce side-effects
  • Composable

Example: pure function

// function foo(x) { return x+1; } 
let foo = x => x+1;

// pure function
foo(1); // 2
foo(1); // 2

Example: side-effect

let flag = false;
let foo = x => { 
  flag = !flag;  // side effect
  return flag ? x+1: 0;
}

// not pure function
foo(1); // 2
foo(1); // 0

Middlewares

  • Sit between Actions and Reducers
  • Used for logging, storage and asynchronous operations
  • Composable

Angular 2

Features

  • Latest Web Standards
  • Simple
  • Lightning fast
  • Works everywhere

Components Tree

source: blog

Components Tree

<root>
  <add-todo>
    <input><button>Add todo</button>
  </add-todo>
  <todo-list>
    <ul>
      <todo id="0" completed="false"><li>buy milk</li></todo>
    </ul>
  </todo-list>
  <filters>
    Show: <filter-link><a>All</a><filter-link> ... 
  </filters>
</root>

Redux Setup

import { App } from './app';
import { createStore } from 'redux';
 
const appStore = createStore(rootReducer);

@NgModule({
  imports: [ BrowserModule ],
  declarations: [
    App, ...APP_DECLARATIONS
  ],
  providers: [
    { provide: 'AppStore', useValue: appStore },
    TodoActions 
  ],
  bootstrap: [ App ]
})
export class AppModule { }

platformBrowserDynamic().bootstrapModule(AppModule);

Adding a new Todo

  • Component subscribe to the Store
  • Component dispatches ADD_TODO action
  • Store executes rootReducer
  • Store notifies Component
  • View updates

Subscribing to the Store

@Component({
  template: 
      `<todo *ngFor="let todo of todos">{{todo.text}}</todo>`
})

export class TodoList implements OnDestroy {
  constructor(@Inject('AppStore') private appStore: AppStore){
    this.unsubscribe = this.appStore.subscribe(() => {
      let state = this.appStore.getState();
      this.todos = state.todos;
    });
  }
  private ngOnDestroy(){
    this.unsubscribe();
  }
}

ADD_TODO Action

// add new todo item
{
  type: ADD_TODO,
  id: 1,
  text: "learn redux",
  completed: false
}

todos Reducer

const todos = (state = [], action) => {
  switch (action.type) {
    case TodoActions.ADD_TODO: 
      return state.concat({ 
          id: action.id,
          text: action.text,
          completed: action.completed });
    default: return state;
  }
}

currentFilter Reducer

const currentFilter = (state = 'SHOW_ALL', action) => {
  switch (action.type) {
    case 'SET_CURRENT_FILTER':
      return action.filter
    default: return state
  }
}

rootReducer

import { combineReducers } from 'redux'

export const rootReducer = combineReducers({
  todos: todos,
  currentFilter: currentFilter
});

New State

{
  todos: [{
    id: 1,
    text: "learn redux",
    completed: false
  }],
  currentFilter: 'SHOW_ALL'
}

Stateless Todo Component

// <todo id="1" completed="true">buy milk</todo>
@Component({
  inputs: ['id', 'completed'],
  template: `
    <li (click)="onTodoClick(id)"
      [style.textDecoration]="completed?'line-through':'none'">
      <ng-content></ng-content>
    </li>`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class Todo { 
  constructor(
    @Inject('AppStore') private appStore: AppStore, 
    private todoActions: TodoActions){ }

  private onTodoClick(id){
    this.appStore.dispatch(this.todoActions.toggleTodo(id));
  }
}

Change Detection

source: blog

Change Detection

source: blog

Redux Dev Tools

Features

  • Save/Restore State
  • Live Debugging
  • Time travel
  • Dispatch Actions

Libraries

  • Angular 2 bindings for Redux
  • Built on top of Redux
  • Compatible w/ DevTools and existing ecosystem
  • Re-implementation of Redux on top Angular 2 and RxJS 5
  • ngrx suite: store, effects, router, db
  • Not compatible w/ DevTools and existing ecosystem

Why use Redux?

Main Benefits

  • Simplified Development
  • Avoids complex dependencies
  • Great Performance
  • Developer Experience

 Thanks!

 Questions

Using Redux and Angular 2

By Gerard Sans

Using Redux and Angular 2

Redux is an architecture made famous by Facebook and React. It is based on Flux so it provides a solid base to create complex applications. In this talk we are going to use a basic application to showcase Redux while using Angular 2.

  • 3,316