SlideShare a Scribd company logo
1 of 136
Download to read offline
Redux for ReactJs
Programmers
by Dr. David Rodenas
State
MVC
M
V
C
http://heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html
Route: /users/123/edit
or Route: /floors/3/rooms/5
or Route: /admin
Users:
{id: 123, name: 'bob', ...}
{id: 124, name: 'alice', ...}
Thermostats:
{floor: 3, room: 5, max: 26, min: 18, ...},
{floor: 5, room: 2, max: 24, min: 22, ...},
Preferences:
dateFormat: 'dd-mm-yyyy'
heater: 'on'
cooler: 'off'
6
Route: /users/123/edit
Users:
{id: 123, name: 'bob', ...}
{id: 124, name: 'alice', ...}
Thermostats:
{floor: 3, room: 5, max: 26, min: 18, ...},
{floor: 5, room: 2, max: 24, min: 22, ...},
Preferences:
dateFormat: 'dd-mm-yyyy'
heater: 'on'
cooler: 'off'
7
https://martinfowler.com/eaaDev/uiArchs.html
three kind of states:
· record state
· session state
· screen state
{
M
V
C
M
V
C
widgets
handlers
M
V
C
handlers
widgets
React
M
V
C
widgets
React
handlers
M
V
C
widgets
React
+ state
handlers
MVC
MVP
MVVC
MVVM
MV*
Two key benefits of MV* are:
• "attach multiple views to a model to provide
different presentations"
• "change the way a view responds to user input
without changing its visual presentation"
https://xkcd.com/927/
“MVC works pretty well
for small applications…
but it doesn’t make room
for new features.”
“Flux is a single direction data flow,
that avoids all the arrows
going on all directions
what makes really
hard to understand the system.”
https://youtu.be/nYkdrAPrdcw?t=10m20s
https://xkcd.com/927/
https://www.youtube.com/watch?v=-jwQ3sGoiXg
https://www.youtube.com/watch?v=-jwQ3sGoiXg
https://www.youtube.com/watch?v=-jwQ3sGoiXg
Origin I - Flux
Flux
21
https://facebook.github.io/flux/
1 * *
*
Flux
// dispatcher.js
const AppDispatcher = new Dispatcher();
export default AppDispatcher;
// Dispatcher API
// AppDispatcher.register(function(action) { ... });
// AppDispatcher.dispatch(action);
22
1
Flux
// stores/todos.js
const _todos = [];
const TodoStore = {...EventEmitter.prototype, {
getAll() {
return _todos;
},
});
AppDispatcher.register(function(action) {
...
TodoStore.emit('change');
});
export default TodoStore;
23
*
Flux
// components/TodoList.js
class TodoList extends React.Component {
constructor() { ... }
componentDidMount() {
TodoStore.addListener('change', this.updateState);
}
componentWillUnmount() {
TodoStore.removeListener('change', this.updateState);
}
updateState = () => {
this.setState({todos: TodoStore.getAll()});
}
render() { ... }
}
24
*
Flux
// components/AddTodo.js
class AddTodo extends React.Component {
constructor() { ... }
handleClick = () => {
AppDispatcher.dispatch({
type: 'ADD_TODO',
text: this.state.value,
});
}
render() { ... }
}
25
*
Origin II - Flux Refactor
27
https://www.youtube.com/watch?v=xsSnOQynTHs
Inspired in Flux
• Like Flux:
• Single direction data-flow
• Has actions
• Actions are dispatched
• Has stores concept
• Views subscribes to stores
28
Inspired in Flux
• Unlike Flux:
• One single store
> Single source of truth
• No dispatcher (dispatch by Store)
• State is computed with reducers
> State is read-only
> Changes are pure functions
• State is injected to views
29
Flux to Redux
const _todos = [];
const TodoStore = {...EventEmitter.prototype, {
getAll() {
return _todos;
},
});
AppDispatcher.register(function(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos.push(text);
TodoStore.emit('change');
}
});
export default TodoStore;
30
Flux to Redux
const _todos = [];
const TodoStore = {...EventEmitter.prototype, {
getAll() {
return _todos;
},
});
AppDispatcher.register(function(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos.push(text);
TodoStore.emit('change');
}
});
export default TodoStore;
31
Flux to Redux
const _todos = [];
const TodoStore = {...EventEmitter.prototype, {
getAll() {
return _todos;
},
});
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos.push(text);
TodoStore.emit('change');
}
});
export default TodoStore;
32
Flux to Redux
const _todos = [];
const TodoStore = {...EventEmitter.prototype, {
getAll() {
return _todos;
},
});
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos.push(text);
TodoStore.emit('change');
}
});
export default TodoStore;
33
Flux to Redux
let _todos = [];
const TodoStore = {...EventEmitter.prototype, {
getAll() {
return _todos;
},
});
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
TodoStore.emit('change');
}
});
export default TodoStore;
34
Flux to Redux
let _todos = [];
const TodoStore = {...EventEmitter.prototype, {
getAll() {
return _todos;
},
});
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
TodoStore.emit('change');
}
});
export default TodoStore;
35
Flux to Redux
let _todos = [];
const TodoStore = new EventEmitter();
export function getState() {
return _todos;
}
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
TodoStore.emit('change');
}
});
export default TodoStore;
36
Flux to Redux
let _todos = [];
const TodoStore = new EventEmitter();
export function getState() {
return _todos;
}
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
TodoStore.emit('change');
}
});
export default TodoStore;
37
Flux to Redux
let _todos = [];
export function getState() {
return _todos;
}
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
}
});
38
Flux to Redux
let _todos = [];
export function getState() {
return _todos;
}
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
}
});
39
Flux to Redux
let _todos = [];
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
}
return _todos;
});
40
Flux to Redux
let _todos = [];
export function handle(action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
}
return _todos;
});
41
Flux to Redux
export function handle(_todos, action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
}
return _todos;
});
42
Flux to Redux
export function handle(_todos, action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
_todos = [..._todos, text];
}
return _todos;
});
43
Flux to Redux
export function handle(state, action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
state = [...state, text];
}
return state;
});
44
Flux to Redux
export function handle(state, action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
state = [...state, text];
}
return state;
});
45
Flux to Redux
export function handle(state, action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
return [...state, text];
default:
return state;
}
});
46
Flux to Redux
export function handle(state = [], action) {
switch (action.type) {
case ActionTypes.TODO_CREATE:
let text = action.text.trim();
return [..._todos, text];
default:
return state;
}
});
47
Redux 101
Actions
Actions
// actions are identified by type
const ADD_TODO = 'ADD_TODO'
// actions are always a JSON equivalent object
const exampleOfAction = {
type: ADD_TODO,
text: 'Learn Redux',
};
// we use function creators for actions
export function addTodo(text) {
return {type: ADD_TODO, text};
};
50
http://redux.js.org/docs/basics/Actions.html#source-code
State
State
// state is one single JSON object
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
id: 123,
text: 'Learn Redux',
completed: false,
},
],
}
// state should be normalized (use pk/fk)
52
Reducers
Compute Action
// execute an action is like:
(previousState, action) => newState
54
Compute Action
const reducer = (previousState, action) => newState
55
Compute Action
const reducer = (previousState, action) => newState
const actions = [
addTodo('buy redux book'),
addTodo('read redux book'),
toggleTodo(3),
setVisibilityFilter(SHOW_ACTIVE),
...
];
56
Compute Action
const reducer = (previousState, action) => newState
const actions = [
addTodo('buy redux book'),
addTodo('read redux book'),
toggleTodo(3),
setVisibilityFilter(SHOW_ACTIVE),
...
];
const finalAppState = actions.reduce(reducer);
57
Reducer
const reducer = (state, action) => {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return {
...state,
visibilityFilter: action.filter,
};
}
};
58
Reducer
const reducer = (state, action) => {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return {
...state,
visibilityFilter: action.filter,
};
default:
return state;
}
};
59
Reducer
const reducer = (state, action) => {
switch (action.type) {
case SET_VISIBILITY_FILTER: ...
case ADD_TODO:
return {
...state,
todos: [
...state.todos,
{ text: action.text, completed: false },
],
};
default:
return state;
}
};
60
Reducer
const reducer = (state, action) => {
switch (action.type) {
case SET_VISIBILITY_FILTER: ...
case ADD_TODO: ...
case TOGGLE_TODO:
return {
...state,
todos: state.todos.map((todo, index) => {
if (index === action.index) {
return {...todo, completed: !todo.completed};
}
return todo;
},
};
default: ...
}
};
61
Initial State
const reducer = (previousState, action) => newState
const actions = [
addTodo('buy redux book'),
addTodo('read redux book'),
toggleTodo(3),
setVisibilityFilter(SHOW_ACTIVE),
...
];
const finalAppState = actions.reduce(reducer);
62
Something is missing: https://developer.mozilla.org/en-US/docs/Web/
JavaScript/Reference/Global_Objects/Array/Reduce#Syntax
Initial State
const reducer = (previousState, action) => newState
const actions = [ ... ];
const initialState = {
visibilityFilter: SHOW_ALL,
todos: [],
};
const finalAppState =
actions.reduce(reducer, initialState);
63
Initial State
const reducer = (previousState, action) => newState
const actions = [ ... ];
const initialState = {
visibilityFilter: SHOW_ALL,
todos: [],
};
const finalAppState =
actions.reduce(reducer, undefined);
64
Reducers
const initialState = { ... };
const reducer = (state, action) => {
if (state === undefined) {
state = initialState;
}
switch (action.type) {
case SET_VISIBILITY_FILTER: ...
case ADD_TODO: ...
case TOGGLE_TODO: ...
default: ...
}
};
65
Reducers
const initialState = { ... };
const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_VISIBILITY_FILTER: ...
case ADD_TODO: ...
case TOGGLE_TODO: ...
default: ...
}
};
66
Splitting Reducers
const initialState = { ... };
const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_VISIBILITY_FILTER: ...
case ADD_TODO: ...
case TOGGLE_TODO: ...
default: ...
}
};
67
Splitting Reducers
const todos = (state, action) => {
switch (action.type) {
case ADD_TODO: ...
case TOGGLE_TODO: ...
default: ...
}
};
const visibilityFilter = (state, action) => {
switch (action.type) {
case SET_VISIBILITY_FILTER: ...
default: ...
}
};
68
Splitting Reducers
const initialState = { ... };
const reducer = (state = initialState, action) => {
state = visibilityFilter(state, action);
state = todos(state, action);
return state;
};
69
Splitting Reducers
const initialState = { ... };
const reducer = (state = initialState, action) => {
return {
visibilityFilter: visibilityFilter(
state.visibilityFilter, action),
todos: todos(state.todos, action)
};
};
70
Splitting Reducers
const reducer = (state = {}, action) => {
return {
visibilityFilter: visibilityFilter(
state.visibilityFilter, action),
todos: todos(state.todos, action)
};
};
71
Splitting Reducers
const visibilityFilter = (state = SHOW_ALL, action) =>
{
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter;
default:
return state;
}
};
72
Splitting Reducers
const todos = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
{ text: action.text, completed: false }
];
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return {...todo, completed: !todo.completed};
}
return todo;
}
default:
return state;
}
};
73
Combine Reducers
const todoApp = (state = {}, action) => {
return {
visibilityFilter: visibilityFilter(
state.visibilityFilter, action),
todos: todos(state.todos, action)
};
};
74
Combine Reducers
const todoApp = (state = {}, action) => {
// Wrong!: creates new object even if nothing changes
return {
visibilityFilter: visibilityFilter(
state.visibilityFilter, action),
todos: todos(state.todos, action)
};
};
75
Combine Reducers
import { combineReducers } from 'redux';
const todoApp = combineReducers({
visibilityFilter,
todos
});
76
http://redux.js.org/docs/basics/Reducers.html#source-code
Store
Store
import { createStore } from 'redux';
const store = createStore(reducer/*, initialState?*/);
// store.getState(): state
// store.dispatch(action)
// store.subscribe(listener): unsubscribeFn
78
http://redux.js.org/docs/basics/Store.html
Dispatching actions
// Do you remember?
const actions = [
addTodo('buy redux book'),
addTodo('read redux book'),
toggleTodo(3),
setVisibilityFilter(SHOW_ACTIVE),
...
];
79
Dispatching actions
// Using the store:
store.dispatch(addTodo('buy redux book'));
store.dispatch(addTodo('read redux book'));
store.dispatch(toggleTodo(3));
store.dispatch(setVisibilityFilter(SHOW_ACTIVE));
80
Redux + React
Bindings
$ npm install --save react-redux
82
http://redux.js.org/docs/basics/UsageWithReact.html
Presentational vs Container
83
Presentational Container
Purpose
How to render things
(html + css)
How things work
(fetch data, updates, ...)
Aware of Redux No Yes
To read data Read data from props Subscribe to Redux state
To change data Invoke callbacks from props Dispatch Redux actions
Written by hand usually by react-redux
Application
• Stores, reducers and actions are pure Redux
• Presentational components are pure React
• You may design them first
• Container components are the glue
• Design which data needs and which actions performs
• Decide which presentational components wrap
84
Presentational Component
function Toggle(props) {
return (
<span onClick={props.onToggle}>
{props.toggle ? 'on' : 'off'}
</span>
);
}
85
Container Component
const mapStateToProps = (state) => ({
toggle: state,
});
const mapDispatchToProps = (dispatch, ownProps) => ({
onToggle: () => dispatch(toggle()),
});
const ToggleContainer = ReactRedux.connect(
mapStateToProps,
mapDispatchToProps
)(Toggle);
86
Providing the Store
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
87
Middleware
Middleware
• What if we want log all actions?
• Log previous state
• Log dispatched action
• Log resulting state
89
Middleware
let action = addTodo('Use Redux')
console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())
90
Middleware
function dispatchAndLog(store, action) {
console.log('dispatching', action)
store.dispatch(action)
console.log('next state', store.getState())
}
dispatchAndLog(store, addTodo('Use Redux'))
91
Middleware
• What if we also want save change states?
• What if we also want report crashes?
• What if we also want to time travel?
92
Middleware
const logger = (store) => (next) => (action) => {
console.log('dispatching', action);
let result = next(action);
console.log('next state', store.getState());
return result;
}
const stateSaver = (store) => (next) => (action) => ...;
const crashReporter = (store) => (next) => (action) => ...;
const timeTraveler = (store) => (next) => (action) => ...;
93
Middleware
const next4 = store.dispatch;
const next3 = logger(store)(next4);
const next2 = stateSaver(store)(next3);
const next1 = crashReporter(store)(next2);
const next0 = timeTraveler(store)(next1);
next0(addTodo('Use Redux'))
94
Middleware
const middlewares = [
logger, // 3
stateSaver, // 2
crashReporter, // 1
timeTraveler, // 0
];
const next = (action) => middlewares.reduce(
(next, middleware) => middleware(store)(next)
, store.dispatch);
next(addTodo('Use Redux'));
95
Middleware
const middlewares = [
timeTraveler, // 0
crashReporter, // 1
stateSaver, // 2
logger, // 3
];
const next = (action) => middlewares.reduceRight(
(next, middleware) => middleware(store)(next)
, store.dispatch);
next(addTodo('Use Redux'));
96
Compose
const greet = (x) => `Hello, ${x}.`;
const emote = (x) => `${x} :)`;
const compose = function(f, g) {
return function(x) {
return f(g(x));
}
}
let happyGreeting = compose(greet, emote);
// happyGreeting("Bob") -> Hello, Bob :).
97
Enhancer
import { applyMiddleware, createStore } from 'redux';
const store = createStore(
todoApp,
/* initialState, */
applyMiddleware(
timeTraveler, // 0
crashReporter, // 1
stateSaver, // 2
logger, // 3
)
);
98
Async
Asynchronous
const PING = 'PING';
const PONG = 'PONG';
const ping = () => ({ type: PING });
const pong = () => ({ type: PONG });
export function pingPong() {
// first ping(), then 1 sec later, pong()
}
100
Asynchronous
const PING = 'PING';
const PONG = 'PONG';
const ping = () => ({ type: PING });
const pong = () => ({ type: PONG });
export function pingPong() {
setTimeout(() => dispatch(pong()), 1000);
return ping();
}
101
Async - Thunk
Asynchronous
const PING = 'PING';
const PONG = 'PONG';
const ping = () => ({ type: PING });
const pong = () => ({ type: PONG });
function pingPong() {
return (dispatch) => {
setTimeout(() => dispatch(pong()), 1000);
return ping();
};
}
103
Recap
const middleware = (store) => (next) => (action) => {
return next(action);
};
104
"Inject" dispatch
const middleware = (store) => (next) => (action) => {
if (typeof action === 'function') {
return action(
store.dispatch,
store.getState
);
}
return next(action);
};
105
Redux-thunk
const thunkMiddleware = (store) => (next) => (action) => {
if (typeof action === 'function') {
return action(
store.dispatch,
store.getState
);
}
return next(action);
};
106
Async - Async-Thunk
Asynchronous
const PING = 'PING';
const PONG = 'PONG';
const ping = () => ({ type: PING });
const pong = () => ({ type: PONG });
function pingPong() {
return (dispatch) => {
setTimeout(() => dispatch(pong()), 1000);
dispatch(ping());
};
}
108
Asynchronous
const PING = 'PING';
const PONG = 'PONG';
const ping = () => ({ type: PING });
const pong = () => ({ type: PONG });
function pingPong() {
return (dispatch) => {
dispatch(ping());
setTimeout(() => dispatch(pong()), 1000);
};
}
109
Asynchronous
const PING = 'PING';
const PONG = 'PONG';
const ping = () => ({ type: PING });
const pong = () => ({ type: PONG });
function pingPong() {
return async (dispatch) => {
dispatch(ping());
await delay(1000);
dispatch(pong());
};
}
110
Redux-thunk
const asyncThunkMiddleware =
(store) => (next) => async (action) =>
{
if (typeof action === 'function') {
return await action(
store.dispatch,
store.getState,
);
}
return next(action);
};
111
Async - Others
Others
• https://redux-saga.js.org/
• https://redux-observable.js.org/
113
Remote I/O
Actions
{ type: 'FETCH_POSTS' }
{
type: 'FETCH_POSTS',
status: 'error',
error: 'Ops'
}
{
type: 'FETCH_POSTS',
status: 'success',
response: { ... }
}
115
Actions
{ type: 'FETCH_POSTS_REQUEST' }
{
type: 'FETCH_POSTS_FAILURE',
error: 'Ops'
}
{
type: 'FETCH_POSTS_SUCCESS',
response: { ... }
}
116
Actions
const requestPosts = () => ({
type: 'FETCH_POSTS_REQUEST'
});
const requestPostsFailure = (error) => ({
type: 'FETCH_POSTS_FAILURE',
error
});
const receivePosts = (response) => ({
type: 'FETCH_POSTS_SUCCESS',
response
});
117
Actions
const fetchPostsRequest = () => ({
type: 'FETCH_POSTS_REQUEST'
});
const fetchPostsFailure = (error) => ({
type: 'FETCH_POSTS_FAILURE',
error
});
const fetchPostsSuccess = (response) => ({
type: 'FETCH_POSTS_SUCCESS',
response
});
118
Async Action
const fetchPosts => () => async (dispatch) => {
dispatch(fetchPostsRequest());
try {
const response = await apiGet('/posts');
dispatch(fetchPostsSuccess(response));
} catch (error) {
dispatch(fetchPostsFailure(error));
}
}
119
Managing State
Normalized Data
http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
http://redux.js.org/docs/recipes/reducers/UpdatingNormalizedData.html
Data Example
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"commenter": {
"id": "2",
"name": "Nicole"
}
}
]
}
122
Unnormalized Data
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"commenter": {
"id": "2",
"name": "Nicole"
}
}
]
}
123
Normalized Example
"posts": {
"123": {
"id": "123",
"author": "1",
"title": "My awesome blog post"
"comments": [ "324" ]
}
},
"comments": {
"324": {
"id": "324",
"commenter": "2"
}
},
"users": {
"1": {
"id": "1",
"name": "Paul",
},
"2": {
"id": "2",
"name": "Nicole",
}
}
124
Normalized State
const state = {
simpleData: ...,
entities: {
posts: { ... },
comments: { ... },
users: { ... },
},
ui: {
selectedComments: [ ... ]
}
};
125
Normalized Reducers
// reducers/entities/comments.js
const comments = (state, action) => { ... };
// reducers/entities/posts.js
const posts = (state, action) => { ... };
// reducers/entities/users.js
const users = (state, action) => { ... };
// reducers/entities/index.js
const entities = combineReducers({comments,posts,users});
// reducers/ui/selectedComments.js
const ui = selectedComments = (state, action) => { ... };
// reducers/ui/index.js
const ui = combineReducers({selectedComments});
// reducers/simpleData.js
const simpleData = (state, action) => { ... };
// reducers/index.js
const app = combineReducers({simpleData, entities, ui});
126
Normalized Updates
function addComment(postId, commentText) {
const commentId = generateId('comment');
return {
type: "ADD_COMMENT",
postId,
commentId,
commentText,
};
};
127
Normalized Updates
const comments = (state = {}, action) => {
...
case ADD_COMMENT:
return {
...state,
[action.commentId]: {
id: action.commentId,
text: action.commentText,
}
};
...
};
128
Normalized Updates
const posts = (state = {}, action) => {
...
case ADD_COMMENT:
const post = state[action.postId];
return {
...state,
[post.id]: {
...post,
comments: [...post.comments, action.commentId],
}
};
...
};
129
Normalized State Alt.
const state = {
simpleData: ...,
entities: {
posts: { byIds: {...}, allIds: [...] },
comments: { byIds: {...}, allIds: [...] },
users: { byIds: {...}, allIds: [...] },
},
ui: {
selectedComments: [ ... ]
}
};
130
Computed Data
http://redux.js.org/docs/recipes/ComputingDerivedData.html
Selectors
• State should be single source of truth
• Normalized data
• No duplicated data
• Computed data can be obtained from state
• Compute under demand
• Memoize results
132
Using reselect
$ npm install --save reselect
133
Create Selector
import { createSelector } from 'reselect';
const getVisibilityFilter = (state) => state.visibilityFilter;
const getTodos = (state) => state.todos;
export const getVisibleTodos = createSelector(
[ getVisibilityFilter, getTodos ],
( visibilityFilter, todos ) => {
switch (visibilityFilter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
);
134
Selectors of Selectors
const getKeyword = (state) => state.keyword
const getVisibleTodosFilteredByKeyword = createSelector(
[ getVisibleTodos, getKeyword ],
( visibleTodos, keyword ) => {
return visibleTodos.filter(
todo => todo.text.indexOf(keyword) > -1
);
)
)
135
Selectors and Containers
const mapStateToProps = (state) => ({
todos: getVisibleTodos(state),
});
const mapDispatchToProps = (dispatch) => ({
onTodoClick: (id) => {
dispatch(toggleTodo(id))
},
});
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
136

More Related Content

What's hot

React, Redux and es6/7
React, Redux and es6/7React, Redux and es6/7
React, Redux and es6/7Dongho Cho
 
Reactive, component 그리고 angular2
Reactive, component 그리고  angular2Reactive, component 그리고  angular2
Reactive, component 그리고 angular2Jeado Ko
 
Adventures In JavaScript Testing
Adventures In JavaScript TestingAdventures In JavaScript Testing
Adventures In JavaScript TestingThomas Fuchs
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testingVisual Engineering
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleAnton Arhipov
 
Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Andres Almiray
 
Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Andres Almiray
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingDavid Rodenas
 
Spock Testing Framework - The Next Generation
Spock Testing Framework - The Next GenerationSpock Testing Framework - The Next Generation
Spock Testing Framework - The Next GenerationBTI360
 
Inside PyMongo - MongoNYC
Inside PyMongo - MongoNYCInside PyMongo - MongoNYC
Inside PyMongo - MongoNYCMike Dirolf
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good TestsTomek Kaczanowski
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
Testing Java Code Effectively
Testing Java Code EffectivelyTesting Java Code Effectively
Testing Java Code EffectivelyAndres Almiray
 
Maintainable JavaScript 2011
Maintainable JavaScript 2011Maintainable JavaScript 2011
Maintainable JavaScript 2011Nicholas Zakas
 

What's hot (20)

React, Redux and es6/7
React, Redux and es6/7React, Redux and es6/7
React, Redux and es6/7
 
JDK Power Tools
JDK Power ToolsJDK Power Tools
JDK Power Tools
 
Reactive, component 그리고 angular2
Reactive, component 그리고  angular2Reactive, component 그리고  angular2
Reactive, component 그리고 angular2
 
Adventures In JavaScript Testing
Adventures In JavaScript TestingAdventures In JavaScript Testing
Adventures In JavaScript Testing
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassle
 
Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017
 
Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving Testing
 
Spock Testing Framework - The Next Generation
Spock Testing Framework - The Next GenerationSpock Testing Framework - The Next Generation
Spock Testing Framework - The Next Generation
 
Celery
CeleryCelery
Celery
 
Inside PyMongo - MongoNYC
Inside PyMongo - MongoNYCInside PyMongo - MongoNYC
Inside PyMongo - MongoNYC
 
Why ruby
Why rubyWhy ruby
Why ruby
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Testing Java Code Effectively
Testing Java Code EffectivelyTesting Java Code Effectively
Testing Java Code Effectively
 
Maintainable JavaScript 2011
Maintainable JavaScript 2011Maintainable JavaScript 2011
Maintainable JavaScript 2011
 
Easy Button
Easy ButtonEasy Button
Easy Button
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
 
Graphql, REST and Apollo
Graphql, REST and ApolloGraphql, REST and Apollo
Graphql, REST and Apollo
 

Similar to Redux for ReactJS Programmers

Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 DreamLab
 
The evolution of redux action creators
The evolution of redux action creatorsThe evolution of redux action creators
The evolution of redux action creatorsGeorge Bukhanov
 
Stay with React.js in 2020
Stay with React.js in 2020Stay with React.js in 2020
Stay with React.js in 2020Jerry Liao
 
Egghead redux-cheat-sheet-3-2-1
Egghead redux-cheat-sheet-3-2-1Egghead redux-cheat-sheet-3-2-1
Egghead redux-cheat-sheet-3-2-1Augustin Bralley
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...DroidConTLV
 
Build Widgets
Build WidgetsBuild Widgets
Build Widgetsscottw
 
Reactивная тяга
Reactивная тягаReactивная тяга
Reactивная тягаVitebsk Miniq
 
FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 
Reactive.architecture.with.Angular
Reactive.architecture.with.AngularReactive.architecture.with.Angular
Reactive.architecture.with.AngularEvan Schultz
 
2018 02-22 React, Redux & Building Applications that Scale | Redux
2018 02-22 React, Redux & Building Applications that Scale | Redux2018 02-22 React, Redux & Building Applications that Scale | Redux
2018 02-22 React, Redux & Building Applications that Scale | ReduxCodifly
 

Similar to Redux for ReactJS Programmers (20)

Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3 Intro to Redux | DreamLab Academy #3
Intro to Redux | DreamLab Academy #3
 
The evolution of redux action creators
The evolution of redux action creatorsThe evolution of redux action creators
The evolution of redux action creators
 
Stay with React.js in 2020
Stay with React.js in 2020Stay with React.js in 2020
Stay with React.js in 2020
 
React redux
React reduxReact redux
React redux
 
Ngrx slides
Ngrx slidesNgrx slides
Ngrx slides
 
React lecture
React lectureReact lecture
React lecture
 
Egghead redux-cheat-sheet-3-2-1
Egghead redux-cheat-sheet-3-2-1Egghead redux-cheat-sheet-3-2-1
Egghead redux-cheat-sheet-3-2-1
 
Road to react hooks
Road to react hooksRoad to react hooks
Road to react hooks
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
 
Build Widgets
Build WidgetsBuild Widgets
Build Widgets
 
Redux vs Alt
Redux vs AltRedux vs Alt
Redux vs Alt
 
Django quickstart
Django quickstartDjango quickstart
Django quickstart
 
Day 1
Day 1Day 1
Day 1
 
Reactивная тяга
Reactивная тягаReactивная тяга
Reactивная тяга
 
FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6FalsyValues. Dmitry Soshnikov - ECMAScript 6
FalsyValues. Dmitry Soshnikov - ECMAScript 6
 
Side effects-con-redux
Side effects-con-reduxSide effects-con-redux
Side effects-con-redux
 
Reactive.architecture.with.Angular
Reactive.architecture.with.AngularReactive.architecture.with.Angular
Reactive.architecture.with.Angular
 
2018 02-22 React, Redux & Building Applications that Scale | Redux
2018 02-22 React, Redux & Building Applications that Scale | Redux2018 02-22 React, Redux & Building Applications that Scale | Redux
2018 02-22 React, Redux & Building Applications that Scale | Redux
 
Ngrx meta reducers
Ngrx meta reducersNgrx meta reducers
Ngrx meta reducers
 

More from David Rodenas

TDD CrashCourse Part2: TDD
TDD CrashCourse Part2: TDDTDD CrashCourse Part2: TDD
TDD CrashCourse Part2: TDDDavid Rodenas
 
TDD CrashCourse Part1: Testing
TDD CrashCourse Part1: TestingTDD CrashCourse Part1: Testing
TDD CrashCourse Part1: TestingDavid Rodenas
 
TDD CrashCourse Part3: TDD Techniques
TDD CrashCourse Part3: TDD TechniquesTDD CrashCourse Part3: TDD Techniques
TDD CrashCourse Part3: TDD TechniquesDavid Rodenas
 
TDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing TechniquesTDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing TechniquesDavid Rodenas
 
Be professional: We Rule the World
Be professional: We Rule the WorldBe professional: We Rule the World
Be professional: We Rule the WorldDavid Rodenas
 
ES3-2020-P2 Bowling Game Kata
ES3-2020-P2 Bowling Game KataES3-2020-P2 Bowling Game Kata
ES3-2020-P2 Bowling Game KataDavid Rodenas
 
Testing, Learning and Professionalism — 20171214
Testing, Learning and Professionalism — 20171214Testing, Learning and Professionalism — 20171214
Testing, Learning and Professionalism — 20171214David Rodenas
 
From high school to university and work
From high school to university and workFrom high school to university and work
From high school to university and workDavid Rodenas
 
Modules in angular 2.0 beta.1
Modules in angular 2.0 beta.1Modules in angular 2.0 beta.1
Modules in angular 2.0 beta.1David Rodenas
 
Freelance i Enginyeria
Freelance i EnginyeriaFreelance i Enginyeria
Freelance i EnginyeriaDavid Rodenas
 
Angular 1.X Community and API Decissions
Angular 1.X Community and API DecissionsAngular 1.X Community and API Decissions
Angular 1.X Community and API DecissionsDavid Rodenas
 
Mvc - Model: the great forgotten
Mvc - Model: the great forgottenMvc - Model: the great forgotten
Mvc - Model: the great forgottenDavid Rodenas
 
(automatic) Testing: from business to university and back
(automatic) Testing: from business to university and back(automatic) Testing: from business to university and back
(automatic) Testing: from business to university and backDavid Rodenas
 
Testing: ¿what, how, why?
Testing: ¿what, how, why?Testing: ¿what, how, why?
Testing: ¿what, how, why?David Rodenas
 

More from David Rodenas (18)

TDD CrashCourse Part2: TDD
TDD CrashCourse Part2: TDDTDD CrashCourse Part2: TDD
TDD CrashCourse Part2: TDD
 
TDD CrashCourse Part1: Testing
TDD CrashCourse Part1: TestingTDD CrashCourse Part1: Testing
TDD CrashCourse Part1: Testing
 
TDD CrashCourse Part3: TDD Techniques
TDD CrashCourse Part3: TDD TechniquesTDD CrashCourse Part3: TDD Techniques
TDD CrashCourse Part3: TDD Techniques
 
TDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing TechniquesTDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing Techniques
 
Be professional: We Rule the World
Be professional: We Rule the WorldBe professional: We Rule the World
Be professional: We Rule the World
 
ES3-2020-P2 Bowling Game Kata
ES3-2020-P2 Bowling Game KataES3-2020-P2 Bowling Game Kata
ES3-2020-P2 Bowling Game Kata
 
ES3-2020-05 Testing
ES3-2020-05 TestingES3-2020-05 Testing
ES3-2020-05 Testing
 
Testing, Learning and Professionalism — 20171214
Testing, Learning and Professionalism — 20171214Testing, Learning and Professionalism — 20171214
Testing, Learning and Professionalism — 20171214
 
Vespres
VespresVespres
Vespres
 
Faster web pages
Faster web pagesFaster web pages
Faster web pages
 
From high school to university and work
From high school to university and workFrom high school to university and work
From high school to university and work
 
Modules in angular 2.0 beta.1
Modules in angular 2.0 beta.1Modules in angular 2.0 beta.1
Modules in angular 2.0 beta.1
 
Freelance i Enginyeria
Freelance i EnginyeriaFreelance i Enginyeria
Freelance i Enginyeria
 
Angular 1.X Community and API Decissions
Angular 1.X Community and API DecissionsAngular 1.X Community and API Decissions
Angular 1.X Community and API Decissions
 
MVS: An angular MVC
MVS: An angular MVCMVS: An angular MVC
MVS: An angular MVC
 
Mvc - Model: the great forgotten
Mvc - Model: the great forgottenMvc - Model: the great forgotten
Mvc - Model: the great forgotten
 
(automatic) Testing: from business to university and back
(automatic) Testing: from business to university and back(automatic) Testing: from business to university and back
(automatic) Testing: from business to university and back
 
Testing: ¿what, how, why?
Testing: ¿what, how, why?Testing: ¿what, how, why?
Testing: ¿what, how, why?
 

Recently uploaded

Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfLivetecs LLC
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 

Recently uploaded (20)

Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdf
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 

Redux for ReactJS Programmers

  • 3. MVC
  • 6. Route: /users/123/edit or Route: /floors/3/rooms/5 or Route: /admin Users: {id: 123, name: 'bob', ...} {id: 124, name: 'alice', ...} Thermostats: {floor: 3, room: 5, max: 26, min: 18, ...}, {floor: 5, room: 2, max: 24, min: 22, ...}, Preferences: dateFormat: 'dd-mm-yyyy' heater: 'on' cooler: 'off' 6
  • 7. Route: /users/123/edit Users: {id: 123, name: 'bob', ...} {id: 124, name: 'alice', ...} Thermostats: {floor: 3, room: 5, max: 26, min: 18, ...}, {floor: 5, room: 2, max: 24, min: 22, ...}, Preferences: dateFormat: 'dd-mm-yyyy' heater: 'on' cooler: 'off' 7 https://martinfowler.com/eaaDev/uiArchs.html three kind of states: · record state · session state · screen state {
  • 13. MVC MVP MVVC MVVM MV* Two key benefits of MV* are: • "attach multiple views to a model to provide different presentations" • "change the way a view responds to user input without changing its visual presentation"
  • 15. “MVC works pretty well for small applications… but it doesn’t make room for new features.” “Flux is a single direction data flow, that avoids all the arrows going on all directions what makes really hard to understand the system.” https://youtu.be/nYkdrAPrdcw?t=10m20s
  • 20. Origin I - Flux
  • 22. Flux // dispatcher.js const AppDispatcher = new Dispatcher(); export default AppDispatcher; // Dispatcher API // AppDispatcher.register(function(action) { ... }); // AppDispatcher.dispatch(action); 22 1
  • 23. Flux // stores/todos.js const _todos = []; const TodoStore = {...EventEmitter.prototype, { getAll() { return _todos; }, }); AppDispatcher.register(function(action) { ... TodoStore.emit('change'); }); export default TodoStore; 23 *
  • 24. Flux // components/TodoList.js class TodoList extends React.Component { constructor() { ... } componentDidMount() { TodoStore.addListener('change', this.updateState); } componentWillUnmount() { TodoStore.removeListener('change', this.updateState); } updateState = () => { this.setState({todos: TodoStore.getAll()}); } render() { ... } } 24 *
  • 25. Flux // components/AddTodo.js class AddTodo extends React.Component { constructor() { ... } handleClick = () => { AppDispatcher.dispatch({ type: 'ADD_TODO', text: this.state.value, }); } render() { ... } } 25 *
  • 26. Origin II - Flux Refactor
  • 28. Inspired in Flux • Like Flux: • Single direction data-flow • Has actions • Actions are dispatched • Has stores concept • Views subscribes to stores 28
  • 29. Inspired in Flux • Unlike Flux: • One single store > Single source of truth • No dispatcher (dispatch by Store) • State is computed with reducers > State is read-only > Changes are pure functions • State is injected to views 29
  • 30. Flux to Redux const _todos = []; const TodoStore = {...EventEmitter.prototype, { getAll() { return _todos; }, }); AppDispatcher.register(function(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos.push(text); TodoStore.emit('change'); } }); export default TodoStore; 30
  • 31. Flux to Redux const _todos = []; const TodoStore = {...EventEmitter.prototype, { getAll() { return _todos; }, }); AppDispatcher.register(function(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos.push(text); TodoStore.emit('change'); } }); export default TodoStore; 31
  • 32. Flux to Redux const _todos = []; const TodoStore = {...EventEmitter.prototype, { getAll() { return _todos; }, }); export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos.push(text); TodoStore.emit('change'); } }); export default TodoStore; 32
  • 33. Flux to Redux const _todos = []; const TodoStore = {...EventEmitter.prototype, { getAll() { return _todos; }, }); export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos.push(text); TodoStore.emit('change'); } }); export default TodoStore; 33
  • 34. Flux to Redux let _todos = []; const TodoStore = {...EventEmitter.prototype, { getAll() { return _todos; }, }); export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; TodoStore.emit('change'); } }); export default TodoStore; 34
  • 35. Flux to Redux let _todos = []; const TodoStore = {...EventEmitter.prototype, { getAll() { return _todos; }, }); export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; TodoStore.emit('change'); } }); export default TodoStore; 35
  • 36. Flux to Redux let _todos = []; const TodoStore = new EventEmitter(); export function getState() { return _todos; } export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; TodoStore.emit('change'); } }); export default TodoStore; 36
  • 37. Flux to Redux let _todos = []; const TodoStore = new EventEmitter(); export function getState() { return _todos; } export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; TodoStore.emit('change'); } }); export default TodoStore; 37
  • 38. Flux to Redux let _todos = []; export function getState() { return _todos; } export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; } }); 38
  • 39. Flux to Redux let _todos = []; export function getState() { return _todos; } export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; } }); 39
  • 40. Flux to Redux let _todos = []; export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; } return _todos; }); 40
  • 41. Flux to Redux let _todos = []; export function handle(action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; } return _todos; }); 41
  • 42. Flux to Redux export function handle(_todos, action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; } return _todos; }); 42
  • 43. Flux to Redux export function handle(_todos, action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); _todos = [..._todos, text]; } return _todos; }); 43
  • 44. Flux to Redux export function handle(state, action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); state = [...state, text]; } return state; }); 44
  • 45. Flux to Redux export function handle(state, action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); state = [...state, text]; } return state; }); 45
  • 46. Flux to Redux export function handle(state, action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); return [...state, text]; default: return state; } }); 46
  • 47. Flux to Redux export function handle(state = [], action) { switch (action.type) { case ActionTypes.TODO_CREATE: let text = action.text.trim(); return [..._todos, text]; default: return state; } }); 47
  • 50. Actions // actions are identified by type const ADD_TODO = 'ADD_TODO' // actions are always a JSON equivalent object const exampleOfAction = { type: ADD_TODO, text: 'Learn Redux', }; // we use function creators for actions export function addTodo(text) { return {type: ADD_TODO, text}; }; 50 http://redux.js.org/docs/basics/Actions.html#source-code
  • 51. State
  • 52. State // state is one single JSON object { visibilityFilter: 'SHOW_ALL', todos: [ { id: 123, text: 'Learn Redux', completed: false, }, ], } // state should be normalized (use pk/fk) 52
  • 54. Compute Action // execute an action is like: (previousState, action) => newState 54
  • 55. Compute Action const reducer = (previousState, action) => newState 55
  • 56. Compute Action const reducer = (previousState, action) => newState const actions = [ addTodo('buy redux book'), addTodo('read redux book'), toggleTodo(3), setVisibilityFilter(SHOW_ACTIVE), ... ]; 56
  • 57. Compute Action const reducer = (previousState, action) => newState const actions = [ addTodo('buy redux book'), addTodo('read redux book'), toggleTodo(3), setVisibilityFilter(SHOW_ACTIVE), ... ]; const finalAppState = actions.reduce(reducer); 57
  • 58. Reducer const reducer = (state, action) => { switch (action.type) { case SET_VISIBILITY_FILTER: return { ...state, visibilityFilter: action.filter, }; } }; 58
  • 59. Reducer const reducer = (state, action) => { switch (action.type) { case SET_VISIBILITY_FILTER: return { ...state, visibilityFilter: action.filter, }; default: return state; } }; 59
  • 60. Reducer const reducer = (state, action) => { switch (action.type) { case SET_VISIBILITY_FILTER: ... case ADD_TODO: return { ...state, todos: [ ...state.todos, { text: action.text, completed: false }, ], }; default: return state; } }; 60
  • 61. Reducer const reducer = (state, action) => { switch (action.type) { case SET_VISIBILITY_FILTER: ... case ADD_TODO: ... case TOGGLE_TODO: return { ...state, todos: state.todos.map((todo, index) => { if (index === action.index) { return {...todo, completed: !todo.completed}; } return todo; }, }; default: ... } }; 61
  • 62. Initial State const reducer = (previousState, action) => newState const actions = [ addTodo('buy redux book'), addTodo('read redux book'), toggleTodo(3), setVisibilityFilter(SHOW_ACTIVE), ... ]; const finalAppState = actions.reduce(reducer); 62 Something is missing: https://developer.mozilla.org/en-US/docs/Web/ JavaScript/Reference/Global_Objects/Array/Reduce#Syntax
  • 63. Initial State const reducer = (previousState, action) => newState const actions = [ ... ]; const initialState = { visibilityFilter: SHOW_ALL, todos: [], }; const finalAppState = actions.reduce(reducer, initialState); 63
  • 64. Initial State const reducer = (previousState, action) => newState const actions = [ ... ]; const initialState = { visibilityFilter: SHOW_ALL, todos: [], }; const finalAppState = actions.reduce(reducer, undefined); 64
  • 65. Reducers const initialState = { ... }; const reducer = (state, action) => { if (state === undefined) { state = initialState; } switch (action.type) { case SET_VISIBILITY_FILTER: ... case ADD_TODO: ... case TOGGLE_TODO: ... default: ... } }; 65
  • 66. Reducers const initialState = { ... }; const reducer = (state = initialState, action) => { switch (action.type) { case SET_VISIBILITY_FILTER: ... case ADD_TODO: ... case TOGGLE_TODO: ... default: ... } }; 66
  • 67. Splitting Reducers const initialState = { ... }; const reducer = (state = initialState, action) => { switch (action.type) { case SET_VISIBILITY_FILTER: ... case ADD_TODO: ... case TOGGLE_TODO: ... default: ... } }; 67
  • 68. Splitting Reducers const todos = (state, action) => { switch (action.type) { case ADD_TODO: ... case TOGGLE_TODO: ... default: ... } }; const visibilityFilter = (state, action) => { switch (action.type) { case SET_VISIBILITY_FILTER: ... default: ... } }; 68
  • 69. Splitting Reducers const initialState = { ... }; const reducer = (state = initialState, action) => { state = visibilityFilter(state, action); state = todos(state, action); return state; }; 69
  • 70. Splitting Reducers const initialState = { ... }; const reducer = (state = initialState, action) => { return { visibilityFilter: visibilityFilter( state.visibilityFilter, action), todos: todos(state.todos, action) }; }; 70
  • 71. Splitting Reducers const reducer = (state = {}, action) => { return { visibilityFilter: visibilityFilter( state.visibilityFilter, action), todos: todos(state.todos, action) }; }; 71
  • 72. Splitting Reducers const visibilityFilter = (state = SHOW_ALL, action) => { switch (action.type) { case SET_VISIBILITY_FILTER: return action.filter; default: return state; } }; 72
  • 73. Splitting Reducers const todos = (state = [], action) => { switch (action.type) { case ADD_TODO: return [ ...state, { text: action.text, completed: false } ]; case TOGGLE_TODO: return state.map((todo, index) => { if (index === action.index) { return {...todo, completed: !todo.completed}; } return todo; } default: return state; } }; 73
  • 74. Combine Reducers const todoApp = (state = {}, action) => { return { visibilityFilter: visibilityFilter( state.visibilityFilter, action), todos: todos(state.todos, action) }; }; 74
  • 75. Combine Reducers const todoApp = (state = {}, action) => { // Wrong!: creates new object even if nothing changes return { visibilityFilter: visibilityFilter( state.visibilityFilter, action), todos: todos(state.todos, action) }; }; 75
  • 76. Combine Reducers import { combineReducers } from 'redux'; const todoApp = combineReducers({ visibilityFilter, todos }); 76 http://redux.js.org/docs/basics/Reducers.html#source-code
  • 77. Store
  • 78. Store import { createStore } from 'redux'; const store = createStore(reducer/*, initialState?*/); // store.getState(): state // store.dispatch(action) // store.subscribe(listener): unsubscribeFn 78 http://redux.js.org/docs/basics/Store.html
  • 79. Dispatching actions // Do you remember? const actions = [ addTodo('buy redux book'), addTodo('read redux book'), toggleTodo(3), setVisibilityFilter(SHOW_ACTIVE), ... ]; 79
  • 80. Dispatching actions // Using the store: store.dispatch(addTodo('buy redux book')); store.dispatch(addTodo('read redux book')); store.dispatch(toggleTodo(3)); store.dispatch(setVisibilityFilter(SHOW_ACTIVE)); 80
  • 82. Bindings $ npm install --save react-redux 82 http://redux.js.org/docs/basics/UsageWithReact.html
  • 83. Presentational vs Container 83 Presentational Container Purpose How to render things (html + css) How things work (fetch data, updates, ...) Aware of Redux No Yes To read data Read data from props Subscribe to Redux state To change data Invoke callbacks from props Dispatch Redux actions Written by hand usually by react-redux
  • 84. Application • Stores, reducers and actions are pure Redux • Presentational components are pure React • You may design them first • Container components are the glue • Design which data needs and which actions performs • Decide which presentational components wrap 84
  • 85. Presentational Component function Toggle(props) { return ( <span onClick={props.onToggle}> {props.toggle ? 'on' : 'off'} </span> ); } 85
  • 86. Container Component const mapStateToProps = (state) => ({ toggle: state, }); const mapDispatchToProps = (dispatch, ownProps) => ({ onToggle: () => dispatch(toggle()), }); const ToggleContainer = ReactRedux.connect( mapStateToProps, mapDispatchToProps )(Toggle); 86
  • 87. Providing the Store import { Provider } from 'react-redux'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ); 87
  • 89. Middleware • What if we want log all actions? • Log previous state • Log dispatched action • Log resulting state 89
  • 90. Middleware let action = addTodo('Use Redux') console.log('dispatching', action) store.dispatch(action) console.log('next state', store.getState()) 90
  • 91. Middleware function dispatchAndLog(store, action) { console.log('dispatching', action) store.dispatch(action) console.log('next state', store.getState()) } dispatchAndLog(store, addTodo('Use Redux')) 91
  • 92. Middleware • What if we also want save change states? • What if we also want report crashes? • What if we also want to time travel? 92
  • 93. Middleware const logger = (store) => (next) => (action) => { console.log('dispatching', action); let result = next(action); console.log('next state', store.getState()); return result; } const stateSaver = (store) => (next) => (action) => ...; const crashReporter = (store) => (next) => (action) => ...; const timeTraveler = (store) => (next) => (action) => ...; 93
  • 94. Middleware const next4 = store.dispatch; const next3 = logger(store)(next4); const next2 = stateSaver(store)(next3); const next1 = crashReporter(store)(next2); const next0 = timeTraveler(store)(next1); next0(addTodo('Use Redux')) 94
  • 95. Middleware const middlewares = [ logger, // 3 stateSaver, // 2 crashReporter, // 1 timeTraveler, // 0 ]; const next = (action) => middlewares.reduce( (next, middleware) => middleware(store)(next) , store.dispatch); next(addTodo('Use Redux')); 95
  • 96. Middleware const middlewares = [ timeTraveler, // 0 crashReporter, // 1 stateSaver, // 2 logger, // 3 ]; const next = (action) => middlewares.reduceRight( (next, middleware) => middleware(store)(next) , store.dispatch); next(addTodo('Use Redux')); 96
  • 97. Compose const greet = (x) => `Hello, ${x}.`; const emote = (x) => `${x} :)`; const compose = function(f, g) { return function(x) { return f(g(x)); } } let happyGreeting = compose(greet, emote); // happyGreeting("Bob") -> Hello, Bob :). 97
  • 98. Enhancer import { applyMiddleware, createStore } from 'redux'; const store = createStore( todoApp, /* initialState, */ applyMiddleware( timeTraveler, // 0 crashReporter, // 1 stateSaver, // 2 logger, // 3 ) ); 98
  • 99. Async
  • 100. Asynchronous const PING = 'PING'; const PONG = 'PONG'; const ping = () => ({ type: PING }); const pong = () => ({ type: PONG }); export function pingPong() { // first ping(), then 1 sec later, pong() } 100
  • 101. Asynchronous const PING = 'PING'; const PONG = 'PONG'; const ping = () => ({ type: PING }); const pong = () => ({ type: PONG }); export function pingPong() { setTimeout(() => dispatch(pong()), 1000); return ping(); } 101
  • 103. Asynchronous const PING = 'PING'; const PONG = 'PONG'; const ping = () => ({ type: PING }); const pong = () => ({ type: PONG }); function pingPong() { return (dispatch) => { setTimeout(() => dispatch(pong()), 1000); return ping(); }; } 103
  • 104. Recap const middleware = (store) => (next) => (action) => { return next(action); }; 104
  • 105. "Inject" dispatch const middleware = (store) => (next) => (action) => { if (typeof action === 'function') { return action( store.dispatch, store.getState ); } return next(action); }; 105
  • 106. Redux-thunk const thunkMiddleware = (store) => (next) => (action) => { if (typeof action === 'function') { return action( store.dispatch, store.getState ); } return next(action); }; 106
  • 108. Asynchronous const PING = 'PING'; const PONG = 'PONG'; const ping = () => ({ type: PING }); const pong = () => ({ type: PONG }); function pingPong() { return (dispatch) => { setTimeout(() => dispatch(pong()), 1000); dispatch(ping()); }; } 108
  • 109. Asynchronous const PING = 'PING'; const PONG = 'PONG'; const ping = () => ({ type: PING }); const pong = () => ({ type: PONG }); function pingPong() { return (dispatch) => { dispatch(ping()); setTimeout(() => dispatch(pong()), 1000); }; } 109
  • 110. Asynchronous const PING = 'PING'; const PONG = 'PONG'; const ping = () => ({ type: PING }); const pong = () => ({ type: PONG }); function pingPong() { return async (dispatch) => { dispatch(ping()); await delay(1000); dispatch(pong()); }; } 110
  • 111. Redux-thunk const asyncThunkMiddleware = (store) => (next) => async (action) => { if (typeof action === 'function') { return await action( store.dispatch, store.getState, ); } return next(action); }; 111
  • 115. Actions { type: 'FETCH_POSTS' } { type: 'FETCH_POSTS', status: 'error', error: 'Ops' } { type: 'FETCH_POSTS', status: 'success', response: { ... } } 115
  • 116. Actions { type: 'FETCH_POSTS_REQUEST' } { type: 'FETCH_POSTS_FAILURE', error: 'Ops' } { type: 'FETCH_POSTS_SUCCESS', response: { ... } } 116
  • 117. Actions const requestPosts = () => ({ type: 'FETCH_POSTS_REQUEST' }); const requestPostsFailure = (error) => ({ type: 'FETCH_POSTS_FAILURE', error }); const receivePosts = (response) => ({ type: 'FETCH_POSTS_SUCCESS', response }); 117
  • 118. Actions const fetchPostsRequest = () => ({ type: 'FETCH_POSTS_REQUEST' }); const fetchPostsFailure = (error) => ({ type: 'FETCH_POSTS_FAILURE', error }); const fetchPostsSuccess = (response) => ({ type: 'FETCH_POSTS_SUCCESS', response }); 118
  • 119. Async Action const fetchPosts => () => async (dispatch) => { dispatch(fetchPostsRequest()); try { const response = await apiGet('/posts'); dispatch(fetchPostsSuccess(response)); } catch (error) { dispatch(fetchPostsFailure(error)); } } 119
  • 122. Data Example { "id": "123", "author": { "id": "1", "name": "Paul" }, "title": "My awesome blog post", "comments": [ { "id": "324", "commenter": { "id": "2", "name": "Nicole" } } ] } 122
  • 123. Unnormalized Data { "id": "123", "author": { "id": "1", "name": "Paul" }, "title": "My awesome blog post", "comments": [ { "id": "324", "commenter": { "id": "2", "name": "Nicole" } } ] } 123
  • 124. Normalized Example "posts": { "123": { "id": "123", "author": "1", "title": "My awesome blog post" "comments": [ "324" ] } }, "comments": { "324": { "id": "324", "commenter": "2" } }, "users": { "1": { "id": "1", "name": "Paul", }, "2": { "id": "2", "name": "Nicole", } } 124
  • 125. Normalized State const state = { simpleData: ..., entities: { posts: { ... }, comments: { ... }, users: { ... }, }, ui: { selectedComments: [ ... ] } }; 125
  • 126. Normalized Reducers // reducers/entities/comments.js const comments = (state, action) => { ... }; // reducers/entities/posts.js const posts = (state, action) => { ... }; // reducers/entities/users.js const users = (state, action) => { ... }; // reducers/entities/index.js const entities = combineReducers({comments,posts,users}); // reducers/ui/selectedComments.js const ui = selectedComments = (state, action) => { ... }; // reducers/ui/index.js const ui = combineReducers({selectedComments}); // reducers/simpleData.js const simpleData = (state, action) => { ... }; // reducers/index.js const app = combineReducers({simpleData, entities, ui}); 126
  • 127. Normalized Updates function addComment(postId, commentText) { const commentId = generateId('comment'); return { type: "ADD_COMMENT", postId, commentId, commentText, }; }; 127
  • 128. Normalized Updates const comments = (state = {}, action) => { ... case ADD_COMMENT: return { ...state, [action.commentId]: { id: action.commentId, text: action.commentText, } }; ... }; 128
  • 129. Normalized Updates const posts = (state = {}, action) => { ... case ADD_COMMENT: const post = state[action.postId]; return { ...state, [post.id]: { ...post, comments: [...post.comments, action.commentId], } }; ... }; 129
  • 130. Normalized State Alt. const state = { simpleData: ..., entities: { posts: { byIds: {...}, allIds: [...] }, comments: { byIds: {...}, allIds: [...] }, users: { byIds: {...}, allIds: [...] }, }, ui: { selectedComments: [ ... ] } }; 130
  • 132. Selectors • State should be single source of truth • Normalized data • No duplicated data • Computed data can be obtained from state • Compute under demand • Memoize results 132
  • 133. Using reselect $ npm install --save reselect 133
  • 134. Create Selector import { createSelector } from 'reselect'; const getVisibilityFilter = (state) => state.visibilityFilter; const getTodos = (state) => state.todos; export const getVisibleTodos = createSelector( [ getVisibilityFilter, getTodos ], ( visibilityFilter, todos ) => { switch (visibilityFilter) { case 'SHOW_ALL': return todos case 'SHOW_COMPLETED': return todos.filter(t => t.completed) case 'SHOW_ACTIVE': return todos.filter(t => !t.completed) } } ); 134
  • 135. Selectors of Selectors const getKeyword = (state) => state.keyword const getVisibleTodosFilteredByKeyword = createSelector( [ getVisibleTodos, getKeyword ], ( visibleTodos, keyword ) => { return visibleTodos.filter( todo => todo.text.indexOf(keyword) > -1 ); ) ) 135
  • 136. Selectors and Containers const mapStateToProps = (state) => ({ todos: getVisibleTodos(state), }); const mapDispatchToProps = (dispatch) => ({ onTodoClick: (id) => { dispatch(toggleTodo(id)) }, }); const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList); 136