53
React.Redux.Real world. Rostislav (BABO)

React. Redux. Real world

Embed Size (px)

Citation preview

React.Redux.Real world.Rostislav (BABO)

BABO2

3

4

5

6

FRONTEND7

Webpack

React, Redux

CSS Modules

8

React9

Бесконечные списки

Screen gif

10

React-ingrid

11Отображать только те элементы, которые умещаются на экране

Universal rendering

const html = ReactDOM.renderToString(<App data={data} />)

res.send(html)

12

Блокирующий вызов

Future

React-dom-stream

res.write(`<!DOCTYPE html>`)const stream = ReactDOMStream.renderToStaticMarkup(<App data={data} />);stream.pipe(res)

13

Redux14

DevTools, Logger,

Time travelling15

Простота16

Redux

const reducer = (oldState = `FOO`, action) => { switch(action.type) { case `UPDATE_FOO`: return `BAR` } return oldState}

const store = createStore(reducer)

17

// подписаться на обновления

store.subscribe(() => { assert.ok(store.getState() === `BAR`)})

// передать измененияstore.dispatch({ type: `UPDATE_FOO`})

// получить текущее состояниеassert.ok(store.getState() === `FOO`)

Tests

const reducer = (oldState, action) => { switch(action.type) { case `ACTION_TYPE`: return action.payload }}

assert.ok(reducer(``, `BAR`) === `BAR`)

18

React + Redux

<Provider store={store}> <ButtonContainer /></Provider>

const Button = ({handleClick, title}) => ( <button onClick={handleClick}>{title}</button>)

19

const mapStateToProps = state => ({ title: state.title})

const mapDispatchToProps = dispatch => ({ handleClick: () => dispatch({ type: `CLICK` })})

const ButtonContainer = connect(mapStateToProps, mapDispatchToProps)(Button)

Immutable20

spread...

const initialState = { foo: `foo`, bar: `bar`}const reducer = (state = initialState, action) => { switch(action.type) { case `UPDATE_BAR`: return { ...state, bar: `baz` } }}

return { ...state, we: { ...state.we, must: { ...state.we.must, go: { ...state.we.must.go, deeper: action.payload } } }}

21

Immutable.js

return { ...state, we: { ...state.we, must: { ...state.we.must, go: { ...state.we.must.go, deeper: action.payload } } }}

return state.setIn([`we`, `must`, `go`, `deeper`], action.payload)

22

Immutable.js

const mapStateToProps = state => ({ foo: state.get(`foo`), bar: state.get(`bar`), ...})

import { Map } from 'immutable'

const map = Map({ foo: `bar`})

const { foo } = map

assert.fails(foo === `bar`)assert.ok(foo === undefined)

23

Seamless-immutable

import Immutable from 'seamless-immutable'

const map = Immutable({foo: `bar`})

const { foo } = map

assert.ok(foo === `bar`)

24

Object.freeze()~5 KB

return state.setIn([`we`, `must`, `go`, `deeper`], action.payload)

Расчеты25

26

Вычисления

const computeAction = data => { const result = compute(data)

return { type: `COMPUTE_ACTION`, payload: result }}

27

Масштабирование

28

state

<Component1 ... />

<Component2 ... />

<Component3 ... />

<Component4 ... />

compute1

compute2

compute3

compute4

Вычисления

29

const mapStateToProps = state => ({ result: compute(state.data)})

Мемоизация | Reselect

import { createSelector } from 'reselect'

const clustersSelector = createSelector( state => state.points, state => state.map.zoom, (points, zoom) => calculateClusters(points, zoom))

30

Actions31

Flux Standard Action

{ type: `DO_SOMETHING`, payload: { foo: `bar` },

meta: { foo: `bar` }}

32

{ type: `DO_SOMETHING`, payload: new Error(), error: true}

Realtime

REALTIME gif

Синхронизация экранов

33

Plain object

34

store.dispatch(...)

store.dispatch(...)

store.dispatch(...)

store.dispatch(...)

SOCKETS

if(action.meta && action.meta.sync) { sockets.emit(action)}

thunk

const asyncAction = () => dispatch => { dispatch({ type: `REQUEST` }) fetch() .then(() => dispatch({type: `REQUEST_SUCCESS`})) .catch(() => dispatch({type: `REQUEST_ERROR`}))}

35

thunk??export const finallySend = () => (dispatch, getState) => { const {phone, location, latlng, description, uploadId} = getState().toJS() dispatch({ type: SEND_REQUEST }) if (isEmpty(latlng)) { if (!location) { dispatch(sendResults({phone, location, description, uploadId})) return dispatch(setStep(`done`)) } geocodeLocation(location).then(payload => { const {lat: photoLat, lng: photoLon} = payload dispatch(sendResults({phone, location, description, uploadId, photoLat, photoLon})) }) } else { const {lat: photoLat, lng: photoLon} = latlng dispatch(sendResults({phone, location, description, uploadId, photoLat, photoLon})) } dispatch(setStep(`done`))}

36

tests?scale?

SAGA37

Sagas

38

ON_CLICK

REQUEST

REQUEST_SUCCESS

function* rootSaga() { yield takeLatest(`CLICK`, request) }

function* request() { try { yield put({type: `REQUEST`}) const payload = yield call(api.requestData) yield put({type: `REQUEST_SUCCESS`, payload}) } catch(e) { yield put({type: `REQUEST_ERROR`}) }}

Тесты

const generator = request()

expect(generator.next().value).toEqual(put({type: `REQUEST`))expect(generator.next().value).toEqual(call(api.requestData))expect(generator.next(dummyResponse).value).toEqual(put({type: `REQUEST_SUCCESS`, payload}))

39

Изоляция40

Проблема?

41

Component

Component

Component

Component

const componentReducer = (state, action) => {

... case `CLICK`:

return state.set(`clicked`, true)

….}

cobmineReducers({ component1: componentReducer, component2: componentReducer ...})

`CLICK`

Переиспользование компонент

class ReusableComponent extends Component { constructor() { this.state = {clicked: false} } handleOnClick() { this.setState({ clicked: true }) } render() { return <button onClick={this.handleOnClick} /> }}

42

43

redux-state

connectState(

mapLocalStateToProps,

mapLocalDispatchToProps,

mergeProps,

componentReducer

)(Component)

44

import {reducer as states} from `redux-state`

combineReducers({ states, ...})

redux-multireducer

45

Component

Component

Component

Component

cobmineReducers({ component: multireducer({ `component1`: componentReducer, `component2`: componentReducer, `component3`: componentReducer, ... }), ...})

`CLICK_reducerKey=component1``CLICK_reducerKey=component2``CLICK_reducerKey=component3`

saga?

ELM architecture

46

47

ELMModel

UpdateCommand

ViewModularity

REDUXStateReducerSagaComponet???

48

UpdaterModel

CommandView

elm

49

Parent

Child

Child

redux-elm

//parentUpdaterimport { Updater } from 'redux-elm';import childUpdater, { init as childInit } from './childUpdater'export const init = () => Immutable({ child1: childInit(), child2: childInit()});export default new Updater(init(), saga) .case(`Child1`, (model, action) => childUpdater(model.child1, action)) .case(`Child2`, (model, action) => childUpdater(model.child2, action)) .toReducer();

50

redux-elm

//Parentimport { forwardTo, view } from 'redux-elm'import ChildView from 'redux-elm'

export default view(({ model, dispatch }) => ( <div> <ChildView model={model} dispatch={forwardTo(dispatch, `Child1`)} /> <ChildView model={model} dispatch={forwardTo(dispatch, `Child2`)} /> </div>));

51

AMAZING REDUX

seamless-immutable/immutable.js

reselect

redux-saga

redux-elm52

Спасибо за внимание!

53