Damian Kowalski
Front-end Developer
The story about state
UI
Filters
Search Results
Sorting
UI
UI
Tracking
Log
Validation
Locale
Tabs
Roundtrip
Earlier/later
Sort/filter business logic
Transit switch
Something happened
State changed
UI is rendered
UI is rendered
Something happened
State changed
UI is rendered
UNI-DIRECTIONAL
DATA FLOW
Application state is:
ONE
SINGLE
JAVASCRIPT
OBJECT
READ-ONLY
Common Redux misconception: state is held in a “giant object”. It’s just object referencing a few other objects. Nothing giant about it.
Dan Abramov (creator of Redux)
CURRENT
STATE
PURE
FUNCTION*
ACTION
NEW STATE
* No side effects
Reducer
const newState = reducer(currentState, action);
// action
{ type: 'ACTION TYPE', /*...*/ }
YOU SHALL NOT MUTATE THE STATE
// state of simple TODO app
const initialState = {
visibilityFilter: 'all',
todos: []
};
function todosReducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
const todoItem = {
text: action.text,
completed: false
};
return R.append(todoItem, state.todos);
default:
return state;
}
}
R.append('tests', ['write', 'more']);
//=> ['write', 'more', 'tests']
R.assoc('c', 3, {a: 1, b: 2});
//=> {a: 1, b: 2, c: 3}
import { createStore } from 'redux';
const store = createStore(reducer, initialState);
Store
getState()
subscribe()
dispatch()
subscribe to state changes in UI
get the current state of the app
dispatch an action to reducers
{
products: [
{
id: 1,
title: 'Product 1',
description: 'Lorem ipsum',
price: 1000
},
{
id: 2,
title: 'Product 2',
description: 'Lorem ipsum',
price: 2000
}
],
user: {
name: 'John Doe',
role: 'admin',
currency: 'EUR'
},
basket: {
items: [1, 1, 2],
totalPrice: 4000
}
}
import { createSelector } from 'reselect'
const productsSelector = state => state.products;
const basketItemsSelector = state => state.basket.items;
const totalProductsSelector = createSelector(
productsSelector,
(items) => R.length(items)
)
const basketViewSelector = createSelector(
productsSelector,
basketItemsSelector,
(products, basketItems) => {
// produce data needed by the view
}
)
Action
Reducer
Store
View
<input ng-model="vm.text">
<a ng-click="vm.addTodo(vm.text)">
add
</a>
<todo-item todo="todo"
ng-repeat="todo in vm.todos">
</todo-item>
{
type: 'ADD TODO',
text: 'Learn Redux'
}
(state, action) => newState
{
visibilityFilter: 'all',
todos: ['Learn Redux']
}
Learn Redux
add
add
Learn Redux
Time Traveling
+
Hot Module Reloading
import ...
angular.module('app', ['ngRedux', 'app.todos'])
.config(($ngReduxProvider) => {
const rootReducer = combineReducers({todos, user});
$ngReduxProvider.createStoreWith(rootReducer);
});
angular.module('app.todos', ['ngRedux'])
.controller('TodosCtrl', ($ngRedux, $scope) => {
let unsubscribe = $ngRedux.connect(sliceOfTheState, availableActions)(this);
$scope.$on('$destroy', unsubscribe);
});
Redux bindings:
{
products: [
{
id: 1,
title: 'Product 1',
description: 'Lorem ipsum',
price: 1000
},
{
id: 2,
title: 'Product 2',
description: 'Lorem ipsum',
price: 2000
}
],
user: {
name: 'John Doe',
role: 'admin',
currency: 'EUR'
},
basket: {
items: [1, 1, 2],
totalPrice: 4000
}
}
function addItemToBasket(itemId) {
return {
type: 'ADD_ITEM_TO_BASKET',
id: itemId
}
}
// basket reducer
function basket(state = initialState, action) {
switch (action.type) {
case 'ADD_ITEM_TO_BASKET':
return R.append(
action.id,
state.basket.items
);
...
default:
return state;
}
}
<div class="product" ng-repeat="product in vm.products">
<product-details item="item"></product-details>
<a ng-click="vm.addItemToBasket(product.id)">
add to basket
</a>
</div>
By Damian Kowalski
The story about state