Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

React. Redux. Real world.

214 views

Published on

Our SPA development experience in BABO with React and Redux

Published in: Software
  • Login to see the comments

React. Redux. Real world.

  1. 1. React.Redux.Real world. Rostislav (BABO)
  2. 2. BABO 2
  3. 3. 3
  4. 4. 4
  5. 5. 5
  6. 6. 6
  7. 7. FRONTEND 7
  8. 8. Webpack React, Redux CSS Modules 8
  9. 9. React 9
  10. 10. Бесконечные списки Screen gif 10
  11. 11. React-ingrid 11 Отображать только те элементы, которые умещаются на экране
  12. 12. Universal rendering const html = ReactDOM.renderToString(<App data={data} />) res.send(html) 12 Блокирующий вызов
  13. 13. Future React-dom-stream res.write(`<!DOCTYPE html>`) const stream = ReactDOMStream.renderToStaticMarkup(<App data={data} />); stream.pipe(res) 13
  14. 14. Redux 14
  15. 15. DevTools, Logger, Time travelling15
  16. 16. Простота 16
  17. 17. 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`)
  18. 18. Tests const reducer = (oldState, action) => { switch(action.type) { case `ACTION_TYPE`: return action.payload } } assert.ok(reducer(``, `BAR`) === `BAR`) 18
  19. 19. 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)
  20. 20. Immutable 20
  21. 21. 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
  22. 22. 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
  23. 23. 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
  24. 24. 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. 25. Расчеты 25
  26. 26. 26
  27. 27. Вычисления const computeAction = data => { const result = compute(data) return { type: `COMPUTE_ACTION`, payload: result } } 27
  28. 28. Масштабирование 28 state <Component1 ... /> <Component2 ... /> <Component3 ... /> <Component4 ... /> compute1 compute2 compute3 compute4
  29. 29. Вычисления 29 const mapStateToProps = state => ({ result: compute(state.data) })
  30. 30. Мемоизация | Reselect import { createSelector } from 'reselect' const clustersSelector = createSelector( state => state.points, state => state.map.zoom, (points, zoom) => calculateClusters(points, zoom) ) 30
  31. 31. Actions 31
  32. 32. Flux Standard Action { type: `DO_SOMETHING`, payload: { foo: `bar` }, meta: { foo: `bar` } } 32 { type: `DO_SOMETHING`, payload: new Error(), error: true }
  33. 33. Realtime REALTIME gif Синхронизация экранов 33
  34. 34. Plain object 34 store.dispatch(...) store.dispatch(...) store.dispatch(...) store.dispatch(...) SOCKETS if(action.meta && action.meta.sync) { sockets.emit(action) }
  35. 35. thunk const asyncAction = () => dispatch => { dispatch({ type: `REQUEST` }) fetch() .then(() => dispatch({type: `REQUEST_SUCCESS`})) .catch(() => dispatch({type: `REQUEST_ERROR`})) } 35
  36. 36. 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?
  37. 37. SAGA 37
  38. 38. 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`}) } }
  39. 39. Тесты 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. 40. Изоляция 40
  41. 41. Проблема? 41 Component Component Component Component const componentReducer = (state, action) => { ... case `CLICK`: return state.set(`clicked`, true) …. } cobmineReducers({ component1: componentReducer, component2: componentReducer ... }) `CLICK`
  42. 42. Переиспользование компонент class ReusableComponent extends Component { constructor() { this.state = {clicked: false} } handleOnClick() { this.setState({ clicked: true }) } render() { return <button onClick={this.handleOnClick} /> } } 42
  43. 43. 43
  44. 44. redux-state connectState( mapLocalStateToProps, mapLocalDispatchToProps, mergeProps, componentReducer )(Component) 44 import {reducer as states} from `redux-state` combineReducers({ states, ... })
  45. 45. 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?
  46. 46. ELM architecture 46
  47. 47. 47 ELM Model Update Command View Modularity REDUX State Reducer Saga Componet ???
  48. 48. 48 Updater Model Command View
  49. 49. elm 49 Parent Child Child
  50. 50. redux-elm //parentUpdater import { 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
  51. 51. redux-elm //Parent import { 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
  52. 52. AMAZING REDUX seamless-immutable/immutable.js reselect redux-saga redux-elm 52
  53. 53. Спасибо за внимание! 53

×