
ABOUT US

LeadScanr - finds leads from social media in real-time
Front-End, ML
Full stack
Ievgen Terpil
Vyacheslav Pytel


chapter 0
INTRO
INITIAL POINT
- Framework without improvements
- File structure
- Scalability
- Build tools

IDEAL APP
- framework with community
- easy to implement new features
- easy to support
- easy to test
- localization
- build tools
chapter 1
HYPE

FLUX

REduX



React Europe
REduX
(state, action) => state

REDUX

REduX
{
type: 'DEPOSIT',
value: 10
}
Action

REduX
function counter(state = 0, action) {
switch (action.type) {
case 'DEPOSIT':
return state + action.value
case 'WITHDRAW':
return state - action.value
default:
return state
}
}
Reducer

Changes are made with pure functions
REduX
State
state tree

Single source of truth
REduX
const store =
createStore(reducer, initialState)
store.subscribe(render)
// -----------------------------------
store.dispatch(action)
STore

State is read-only
REduX
const App = ({ state }) => {
<div>
Balance: {state}
</div>
}
VIEW

VIEW LAYER
........


REACT AS VIEW LAYER

REACT AS VIEW LAYER
Account = ({ balance, onDepositClick }) => {
<div>
<button onClick={onDepositClick}>
deposit
</button>
<div>Balance: {balance}</div>
</div>
}
REACT AS VIEW LAYER
connect(
mapStateToProps,
mapDispatchToProps
)(Account)
function mapStateToProps(state, ownProps) {
return { balance: state.balance }
}
function mapDispatchToProps(dispatch) {
return {
onDepositClick: () => dispatch(deposit())
}
}

REACT AS VIEW LAYER
@connect(({ Subscriptions, Profile }) => ({
currentPlan: Subscriptions.get('currentPlan'),
userName: Profile.get('userName')
}))
export default class Subscriptions extends React.Component {
static propTypes = {
dispatch: PropTypes.func.isRequired,
userName: PropTypes.string,
currentPlan: PropTypes.object
}
...
}
our case with ES7
decorator
DUMB AND SMART
Dumb (Presentational)
Smart (Container)
logic
Redux's connect
binds cb for dumb
DOM markup and styles
reusable
your mini Bootstrap
SIDE EFFECTS

SIDE EFFECTS

SIDE EFFECTS - BASE APPROACH
{ type: 'FETCH_ACCOUNT_REQUEST' }
{ type: 'FETCH_ACCOUNT_SUCCESS', account: { ... } }
{ type: 'FETCH_ACCOUNT_FAILURE', error: 'Oops' }
actions
action creators
function receiveAccount(account) {
return {
type: FETCH_ACCOUNT_SUCCESS,
account
}
}
SIDE EFFECTS - BASE APPROACH
let getAccount = id => dispatch => {
dispatch(requestAccount(id));
return fetch('/account', id)
.then(account => dispatch(receiveAccount(account)))
.catch(error => dispatch(throwError(error)));
};
complex action creator
API request
import { CALL_API } from `redux-api-middleware`;
{
[CALL_API]: {
endpoint: 'api/account',
method: 'GET',
types: ['REQUEST', 'SUCCESS', 'FAILURE']
}
}
action creators
SIDE EFFECTS - LESS BOILERPLATE
declarative
SIDE EFFECTS - LESS BOILERPLATE

chapter 2
PRODUCTION

FEATURE FOLDERS

FEATURE FOLDERS

view
actions
reducers
i
i
i
feature1
feature2
feature3
FEATURE FOLDERS

view
actions
reducers
i
i
i
feature1
feature2
feature3
set of standart components
FEATURE FOLDERS

view
actions
reducers
i
i
i
feature1
feature2
feature3
set of standart components
main reducer
FEATURE FOLDERS

view
actions
reducers
i
i
i
feature1
feature2
feature3
set of standart components
main reducer
all actions
FEATURE FOLDERS
our solutions
import reducers from './features/**/reducers.js';
yo redux-component
import actions from './features/**/actions.js';
account/
Account.jsx
actions.js
reducres.js
button/
Button.jsx
b-button.scss
Smart (feature)
Dump
- if it receives a promise, it will dispatch the resolved value of the promise
export function getAccount() {
return async (api) => {
return {
type: events.ACCOUNT_READY,
account: await api.options.account.get()
};
};
}
our case with ES7
SIDE EFFECTS - ADVANCED USAGE
Action Creator
Service 1
Service 2
Service 3
A
A
A
SIDE EFFECTS - ADVANCED USAGE
Action Creator
Service 1
Service 2
Service 3
A
A
A
SIDE EFFECTS - ADVANCED USAGE
Action Creator
Action Creator
SIDE EFFECTS - ADVANCED USAGE

applyMiddlewares(middleware1, middleware2, ...)
dispatch([
startProcessCard(),
setCreditCard(card),
getOffers(),
buy(plan.get('id')),
pushState(null, '/account', {})
]);
async waterfall
SIDE EFFECTS - ADVANCED USAGE
chapter 3
SAGA

SAGA
orchestrating complex/asynchronous operations
Saga
Service 1
Service 2
Service 3
REDUX-SAGA
Generator functions (ES6) as action creators
1
function* fetchAccount() {
const account = yield Api.fetch('/account')
console.log(account)
}
function* watchFetchAccount() {
yield* takeEvery('ACCOUNT_REQUESTED', fetchAccount)
}
REDUX-SAGA
Declarative Effects
{
CALL: {
fn: Api.fetch,
args: ['./account']
}
}
2
yield only a description of
the function invocation
import { call } from 'redux-saga/effects'
function* fetchAccount() {
const account = yield call(Api.fetch, '/account')
// ...
}
- making our code testable
REDUX-SAGA
Dispatching actions
import { call } from 'redux-saga/effects'
function* fetchAccount(dispatch) {
const account = yield call(Api.fetch, '/account')
dispatch({ type: 'ACCOUNT_RECEIVED', account })
}
import { call, put } from 'redux-saga/effects'
function* fetchAccount() {
const account = yield call(Api.fetch, '/account')
yield put({ type: 'ACCOUNT_RECEIVED', products })
}
REDUX-SAGA
Testing
const iterator = fetchAccount()
assert.deepEqual(
iterator.next().value,
call(Api.fetch, '/account')
)
// create a fake response
const account = { balance: 10 }
// expects a dispatch instruction
assert.deepEqual(
iterator.next(account).value,
put({ type: 'ACCOUNT_RECEIVED', account })
)}
REDUX-SAGA API
takeEvery
takeLatest
Saga
take
put
call
A
Api
Dispatcher
fork
Saga
cancel
CONCLUSION
flux
redux
redux:
(state, action) => state
CONCLUSION
flux
redux
redux:
(state, action) => state
use feature folders
create collection of Dumb components
side-effects:
easy
complex
redux-thunk
redux-promise
redux-saga
i
THANK YOU FOR YOUR ATTENTION
Ievgen Terpil
Vyacheslav Pytel





Redux. From twitter hype to production
By Jenya Terpil
Redux. From twitter hype to production
- 64,842