How & why use Redux with Angular 1

A.K.A. the beauty and the beast

1) redux recap

 

2) redux with angular


3) feedback on real app

 

1) redux recap

state

An action is dispatched

And goes though reducers

new state

component

Components are connected to the state

(previousState, action) => newState

 

Three principles

  • Single source of truth
  • State is read-only
  • Changes are made with pure functions

2) redux with angular

package.json

    "immutable": "^3.8.1",
    "ng-redux": "^3.4.0-beta.1",
    "redux": "^3.6.0",
    "redux-thunk": "^2.1.0",

    "reselect": "^2.5.3",

 

  this[CREATE_START] = state => state;

  this[CREATE_SUCCESS] = (state, {todo}) => {
    return state.set(todo.id, immutable.fromJS(todo));
  };

  this[CREATE_ERROR] = state => state;


  this.reducer = (state = initialState, {type, payload}) => {
    return typeof this[type] === 'function' ? this[type](state, payload) : state;
  };

reducer example

  this.reducer = function (state = initialState, action) {
    switch (action.type) {
    case 'CREATE_START':
      return state;
    case 'CREATE_SUCCESS':
      return state.set(action.todo.id, immutable.fromJS(action.todo));
    case 'CREATE_START':
      return state;
    default:
      return state;
    }
  };

another kind of reducer (with switch)


  this.create = todoName => dispatch => {
    dispatch(this.createStart());
    
    // Yay! Can invoke sync or async actions with `dispatch`
    // Thanks to redux-thunk
    $http({
      url: '/api/todo',
      method: 'POST',
      data: {
        name: todoName
      }
    })
      .then(response => {
        dispatch(this.createSuccess(response.data));
      })
      .catch(error => {
        dispatch(this.createError(error));
      })
  };

actions example

export default function TodolistController($ngRedux, todoActions) {
  $ngRedux.dispatch(todoActions.fetch());

  this.$onDestroy = $ngRedux.connect(state => {
    const todos = state.todos.toArray();

    return {
      todos
    };
  })(this);
}

controller (connect + dispatch)

import { createSelector } from 'reselect'

const shopItemsSelector = state => state.shop.items

const taxPercentSelector = state => state.shop.taxPercent

const subtotalSelector = createSelector(
  shopItemsSelector,
  items => items.reduce((acc, item) => acc + item.value, 0)
)

const taxSelector = createSelector(
  subtotalSelector,
  taxPercentSelector,
  (subtotal, taxPercent) => subtotal * (taxPercent / 100)
)
let exampleState = {
  shop: {
    taxPercent: 8,
    items: [
      { name: 'apple', value: 1.20 },
      { name: 'orange', value: 0.95 },
    ]
  }
}
console.log(subtotalSelector(exampleState)) // 2.15
console.log(taxSelector(exampleState))      // 0.172

reselect

Demo time

A.K.A. another todo app

 

3) feedback on real app

Pros

  • Easy to implement feature that would be complicated otherwise (loading states, undo/redo, deactivating some API calls ...)
  • Awesome debugger extension
  • Makes it easier to structure a big application 

Cons

  • Chrome extension is laggy
  • Immutable JS ?! (perf issues, can't be used in ng-repeat, unclear if using immutable or object)
  • Need to convince people

When to use redux

  • One view depends on multiple API calls
  • Multiple source of data (http, websocket) that needs to be unified

In general, use Redux when you have reasonable amounts of data changing over time, you need a single source of truth, and you find that approaches like keeping everything in a top-level React component's state are no longer sufficient.

When not to use redux

  • The app is straightforward and it would be overkill
  • In half of the application
  • For animations

Thank you !

Questions ?

https://github.com/william26/ng-redux-tuto

http://stackoverflow.com/questions/39227832/advantages-of-using-immutable-js-over-object-assign-or-spread-operators

https://github.com/reactjs/reselect

http://redux.js.org/

http://www.meetic.fr/d/onlines

Made with Slides.com