Gian Marco Toso
Drinking coffee and saving the world. Software Engineer and professional geek
and how it can be used with Angular2
Gian Marco Toso
@gianmarcotoso
gianmarcotoso
gianmarcotoso.com
polarityb.it
Software Engineer, Codesmith
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
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...
One way data flow
A (better) FLUX variant
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
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
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 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
Each action is synchronously dispatched to the store
... but we know that things are mostly asynchronous!
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...
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
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
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...
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?
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
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[]
}
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
}
}
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
})
Our store will allow us to dispatch actions and access the state
const store: Store<AppState> = createStore(AppState)(
postsReducer
)
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 }
]
})
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
... and we can dispatch actions!
// ...
addPost() {
const post: Post = new Post({title: "Hello!"})
this.store.dispatch(PostsActions.addPost(post)
}
}
You should also check out angular2-redux and ng2-redux
By Gian Marco Toso
Slides for my talk at the AngularConf 2016 in Torino
Drinking coffee and saving the world. Software Engineer and professional geek