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,260