Redux: An Introduction

and how it can be used with Angular2

Gian Marco Toso

@gianmarcotoso

gianmarcotoso

gianmarcotoso.com

polarityb.it

Software Engineer, Codesmith

About Me

Born and raised in Turin, Italy

Gian Marco Toso

@gianmarcotoso

gianmarcotoso

gianmarcotoso.com

polarityb.it

Software Engineer, Codesmith

MSc in Computer Engineering 

Self Employed Software Engineer

Researcher at ISMB

PHP and Javascript Developer

My Tech Stack

Docker

Gian Marco Toso

@gianmarcotoso

gianmarcotoso

gianmarcotoso.com

polarityb.it

Software Engineer, Codesmith

PHP 5.6/7.0 || NodeJS

Laravel || Express

ReactJS + Redux

I am not an Angular2 developer!

Redux is a FLUX variant...

And FLUX is a pattern!

FLUX

One way data flow

REDUX

A (better) FLUX variant

One single Store

The standard FLUX implementation expects a store for each "domain" of the application

Each "domain" is managed by a specific Reducer

Redux has only one store, containing a single state which is itself divided in as many "domains" as required

Three Main Components

The State

The one single source of truth for your Redux application.

Actions

They are fed to the store and produce a new state by passing through one or more Reducers

Reducers

They are "fed" actions and produce a new state

Reducers are pure functions

A reducer is a pure function that takes in the current state and an action and returns a new state

import initialState from './DefaultState'

export default postsReducer = (state = initialState, action) => {
    switch (action.type) {
        case POPULATE_POSTS: {
            return { 
                ...state, 
                posts: action.posts
            }
        }

        default:
            return state
    }
}

Actions and Action Creators

Actions are plain objects, containing a type and, optionally, a payload

import {
    POPULATE_POSTS
} from './Actions'

export function populatePosts(posts) {
    return {
        type: POPULATE_POSTS,
        posts
    }
}

Actions are returned from Action Creators, which are functions that return the action itself

Actions are synchronous

Each action is synchronously dispatched to the store

... but we know that things are mostly asynchronous!

Enter: Middlewares

When an action is dispatched, it goes through every middleware registered on the store before reaching the reducers

Middlewares can be used for a lot of things, such as asynchronous API calls, logging, debugging...

The Thunk Middleware

One way to solve the asynchronous API call / synchronous action problem is to use the thunk middleware

The Thunk middleware intercepts actions that are functions instead of plain objects and calls them instead of forwarding them to the reducers

The Thunk Middleware

These functions, or thunks, receive the dispatch function as their argument, so they can dispatch a regular action (or another thunk!) when they are done

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

This is the actual code of the thunk middleware as written in the `redux-thunk` library

Putting things together

To make a Redux application we need:

 

- At least one reducer

- Some actions that can be fed to the reducer

- A store to access the state and dispatch actions

One thing is still missing...

Using Redux with Angular2

In order to use Redux with Angular2 we need to:

 

- Somehow provide an Angular2 component access to the store, so that it can dispatch actions

- Have some way to tell an Angular2 component when the state changes, so that it can update itself if required

How?

1. Import Redux

Redux has TypeScript definitions. This means that we can easily import it into our ng2 project!

Docs: https://github.com/reactjs/redux/blob/master/docs/Glossary.md

import { Component } from '@angular2/core'

/* ... other imports ... */

import { 
    createStore,
    Store
} from 'redux

2. Define an Application State

The application state is our single source of truth; we need to decide how it's made

import { Post } from './Post'

export interface AppState {
    posts: Post[]
}

3. Define some reducers...

Our reducers will create a new state for every action the reduce

import {
    ADD_POST
} from './Actions'

const defaultState: AppState = { 
    posts: [] 
}

export const postsReducer(state: AppState = defaultState, action: Action): AppState => {
    switch(action.type) {
        case ADD_POST: {
            return [...state, action.post]
        }

        default: return state
    }
}

4. ... and some actions!

We need actions to send to our reducers, and Action Creators to create these actions

import {
    Action,
    ActionCreator
} from 'redux'

// Action - always namespace!
export const ADD_POST = 'POSTS@ADD'

// Action Creator
export const addPost: ActionCreator<Action> = (post: Post) => ({
    type: ADD_POST,
    post: post
})

5. Create a Store

Our store will allow us to dispatch actions and access the state

const store: Store<AppState> = createStore(AppState)(
    postsReducer
)

6. Provide the store the our app

Using a provider will allow us to make the store accessible from within our components!

// useValue and not useClass since we want the
// instance we created during the previous step
@NgModule({
    declarations: [
        BlogApp
        // ...
    ],
    // ...
    providers: [
        // ...
        { provide: 'AppStore', useValue: store }
    ]
})

7a. Use Redux within a component!

We need to subscribe to the store...

export default class BlogComponent {
    posts: Post[]

    constructor(@Inject('AppStore') private store: Store<AppState>) {
        store.subscribe(() => this.readState())
        this.readState()
    }

    readState() {
        const state: AppState = this.store.getState() as AppState
        this.posts = state.posts  
    }

    // ... continued

7b. Use Redux within a component!

... and we can dispatch actions!

    // ...
    
    addPost() {
        const post: Post = new Post({title: "Hello!"})

        this.store.dispatch(PostsActions.addPost(post)
    }
}

Why Redux?

Other resources

You should also check out angular2-redux and ng2-redux

Thank you!

Questions?

Redux: An Introduction

By Gian Marco Toso

Redux: An Introduction

Slides for my talk at the AngularConf 2016 in Torino

  • 439
Loading comments...

More from Gian Marco Toso