INTRODUCTION TO REDUX

  1. Open a browser on any laptop, tablet or smartphone
  2. Go to slido.com
  3. Enter the event code #LC149

PREDICTABLE

STATE CONTAINER

FOR JAVASCRIPT APPLICATION

DATA

CONSISTENCY

PREDICTABLE

DEBUGGABILITY

3 PILLARS OF REDUX

3 PILLARS OF REDUX

  • SINGLE SOURCE OF TRUTH

  • STATE IS READ ONLY

  • CHANGES ARE MADE WITH PURE FUNCTIONS

SINGLE SOURCE OF TRUTH

STATE IS READ-ONLY

CHANGES ARE MADE WITH PURE FUNCTIONS

// pure functions
function square(x) {
    return x * x;
}
function squareAll(items) {
    return items.map(square);
}

// impure functions
let TWO = 2;
function square(x) {
    updateDb(x);
    return x * TWO;
}
function squareAll(items) {
    for(let i=0; i<items.length; i++) {
        items[i] = square(items[i]);
    }
    return items;
}

3 PILLARS OF REDUX

  • SINGLE SOURCE OF TRUTH

  • STATE IS READ ONLY

  • CHANGES ARE MADE WITH PURE FUNCTIONS

require("redux")

Writing Reducers

Avoiding array mutations

push(), pop(), unshift(), splice()

// adding an item

// NO
function addItem(items, item) {
    items.push(item);
    return items;
}

// YES
function addItem(items, item) {
    return items.concat([item]);
}

function addItem(items, item) {
    return [...items, item];
}
// removing an item

// NO
function removeItem(items, index) {
    items.splice(index);
    return items;
}

// YES
function removeItem(items, index) {
    return items.slice(0, index)
            .concat(item.slice(index + 1));
}

function removeItem(items, index) {
    return [
        ...items.slice(0, index), 
        ...items.slice(index + 1)
    ];
}
// increment an item

// NO
function incrementItem(items, index) {
    items[index]++;
    return items;
}

// YES
function incrementItem(items, index) {
    return items.slice(0, index)
            .concat([items[index] + 1])
            .concat(item.slice(index + 1));
}

function incrementItem(items, index) {
    return [
        ...items.slice(0, index), 
        items[index] + 1,
        ...items.slice(index + 1)
    ];
}

Avoiding object mutations

// setting a value

// NO
function toggleTodo(todo) {
    todo.completed = !todo.completed;
    return todo;
}

// YES
function toggleTodo(todo) {
    return {
        id: todo.id,
        text: todo.text,
        completed: !todo.completed,
    };
}

function toggleTodo(todo) {
    return Object.assign({}, todo, { completed: !todo.completed });
}
// ES6
function toggleTodo(todo) {
    return {
        ...todo,
        completed: !todo.completed,
    };
}

Write a reducer: Adding Todo

Write a reducer:

Change Visibility

Write a reducer:

Toggle Todo

function reducer(state = INITIAL_STATE, action) {
  switch(action.type) {
    case 'TOGGLE_TODO': {
        return {
            ...state,
            todos: state.todos.map(todo => {
                if (todo.id === action.id) {
                    return {
                        ...todo,
                        done: !todo.done,
                    };
                } else {
                    return todo;
                }
            });
        }
    }
  }
}
function reducer(state = INITIAL_STATE, action) {
  switch(action.type) {
    case 'TOGGLE_TODO': {
        return {
            ...state,
            todos: state.todos.map(todo => {
                if (todo.id === action.id) {
                    return {
                        ...todo,
                        done: !todo.done,
                    };
                } else {
                    return todo;
                }
            });
        }
    }
  }
}
function todoReducer(todo, action) {
  switch(action.type) {
    case 'TOGGLE_TODO':
      if (todo.id === action.id) {
        return {
          ...todo,
          done: !todo.done,
        };
      } else {
        return todo;
      }        
  }
}

function todosReducer(state = INITIAL_STATE, action) {
  switch(action.type) {
    case 'EDIT_TODO':
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map(todo => todoReducer(todo, action)),
      }
  }
}
{
   "todos": Todo[],
   "visibiltiyFilter": "SHOW_ALL" | 
                       "SHOW_ACTIVE" | 
                       "SHOW_COMPLETED",
}
function todosReducer(state = [], action) {
    // ...
    return state;
}

function visibilityFilterReducer(state = 'SHOW_ALL', action) {
    // ...
    return state;
}

function reducer(state, action) {
  return {
    todos: todosReducer(state, action),
    visibilityFilter: visibilityFilterReducer(state, action),
  }
}

combineReducers

const reducer = combineReducer({
    visibilityFilter: visibilityFilterReducer,
    todos: todosReducer,
});

Redux + React

require('react-redux')

connect(
    mapStateToProps,
    mapDispatchToProps
) (Component)
connect(
    (state) => {
        return {
            // state props
            items: state.items,
        };
    },
    (dispatch) => {
        return {
            // dispatch props
            toggleItem: (index) => 
                dispatch({ type: 'TOGGLE', index: index })
        };
    }
) (Component)
// itemsSelector can be reused
function itemsSelector(state) {
    return state.items;
}

connect(
    (state) => {
        return {
            // state props
            items: itemsSelector(state),
        };
    },
    (dispatch) => {
        return {
            // dispatch props
            toggleItem: (index) => 
                dispatch({ type: 'TOGGLE', index: index })
        };
    }
) (Component)

selector

}

Let mapStateToProps Reshape the Data from the Store

mapStateToProps Functions Should Be Fast

Return Values Determine If Your Component Re-Renders

 

Only Return New Object References If Needed

connect(
    (state) => {
        return {
            // state props
            items: state.items,
        };
    },
    (dispatch) => {
        return {
            // dispatch props
            toggleItem: (index) => 
                dispatch({ type: 'TOGGLE', index: index })
        };
    }
) (Component)
connect(
    (state) => {
        return {
            // state props
            items: state.items,
        };
    },
    {
        // dispatch props
        toggleItem: (index) => 
            ({ type: 'TOGGLE', index: index })
    }
) (Component)
// can reuse this action
function toggleAction(index) {
    return { type: 'TOGGLE', index: index };
}

connect(
    (state) => {
        return {
            // state props
            items: state.items,
        };
    },
    {
        // dispatch props
        toggleItem: toggleAction,
    }
) (Component)

actionCreator

}

SUMMARY

Redux + React

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

dispatch

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

ACTION

REDUCER

STORE

SELECTOR

REACT COMPONENTS

1 WAY DATA FLOW

Asynchronous Redux

ACTIONS

  • Requesting Action
  • Request Success Action
  • Request Fail Action

Redux Middleware

import { createStore, applyMiddleware } from 'redux'

const store = createStore(
  reducers,
  // applyMiddleware() tells createStore() how to handle middleware
  applyMiddleware(
    middleware1,
    middleware2,
  )
)

"redux-thunk"

thunk - past tense of "think"

 

 

- The New Hacker's Dictionary

Redux DevTools

Resources

Q & A

  1. Open a browser on any laptop, tablet or smartphone
  2. Go to slido.com
  3. Enter the event code #LC149

REDUX

By Li Hau Tan