Redux

Bob Bijvoet

Front-end Chapter Lead

Building Blocks 1

Problems with application state today

Looking at the future

A predictable state container for JavaScript apps.

Single source of truth

The state of your whole application is stored in an object tree within a single store.

 

 

State is read-only

The only way to mutate the state is to emit an action, an object describing what happened.

Changes are made with pure functions

To specify how the state tree is transformed by actions, you write pure reducers.

Pure function

  • Given the same input, will always return the same output.
  • Produces no side effects.
  • Relies on no external state.

Store

The place where all your application state lives

//State
{
     items:[],
     done:true
}

Actions

 

 Tells store that something happened and that the store should update itself in response.

//Action
{
     type:'SOME_ACTION',
     payload:{}
}

//Example
{
     type:'ADD_TO_CART',
     payload:{
        id:1,
        price:42
     }
}

Reducers

 Responsible for mutating the store state when actions are dispatched

reducer(state, action) {
     switch (action.type) {
          case 'SOME_ACTION':
          //Update state
     }
     return state;
}

reducer(state, action) {
     switch (action.type) {
          case 'ADD_TO_CART':
              cart.push(action.payload)
          break;  
     }
     return state;
}

Middleware

Provides the ability to create functions that extend the library.

Container

  • Are concerned with how things work.
  • May contain both presentational and container components.
  • Provide the data and behavior to presentational or other container components.
  • Are often stateful, as they tend to serve as data sources.

Presentational

  • Are concerned with how things look.
  • Don’t specify how the data is loaded or mutated.
  • Rarely have their own state (when they do, it’s UI state rather than data).

Component architecture

Component architecture

Sprint app

State

State

sprint:{
    number: 0,
    burnedPoints: 0,
    done:false
};

stories:[{
    id:'1'
    points:1,
    description:'',
    done:false  
}]

Actions

{
    type: 'STORY_DONE',
    payload: {
        id: story.id,
        points:story.points
    }
};

{
    type:'SPRINT_DONE'
};

Actions

function storyDone(story) {
    return {
        type: 'STORY_DONE',
        payload: {
            id: story.id,
            points:story.points
        }
    };
}

Action creator

Reducer



case 'STORY_DONE':
    var story = state.stories.find(function(story) {
       return story.id === action.payload.id;
    });

    if (story) {
        story.done = true;
    }

    state.sprint.burnedPoints += action.payload.points;

    break;

case 'SPRINT_DONE':
    state.sprint.done = true;

Reducer

Middleware

Middleware

Promise middleware

//Use of promise middleware

//Dispatch action with promise inside the payload 
{
    type: 'FETCH_PRODUCTS',
    payload: {
        promise: fetch('example.com/products') 
}

//Automagically results in action:
//FETCH_PRODUCTS_PENDING

//And results in either: 
//FETCH_PRODUCTS_FULFILLED or FETCH_PRODUCTS_REJECTED 



//Then handle the actions inside of the reducer
case 'FETCH_PRODUCTS_PENDING':
    state.loading = true;
    state.error = false;

case 'FETCH_PRODUCTS_FULFILLED':
    state.loading = false;
    state.products = action.payload.products;

case 'FETCH_PRODUCTS_REJECTED':
    state.loading = false;
    state.error = true;

Benefits

  • One place for state
  • One place for app logic
  • Clear list of actions
  • Use actions for analytics
  • Asynchronous state representation
  • Powerful middleware and easy debugging
  • Easily testable

Testing

Goodies

Let's start hacking

Switch to ing-assignment branch

 

https://github.com/bobbijvoet/ng-redux-session

Redux

By Bob Bijvoet

Redux

  • 1,397