Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React+Redux @ Scale

React+Redux @ Scale

Daniel Cousineau

June 26, 2017
Tweet

More Decks by Daniel Cousineau

Other Decks in Programming

Transcript

  1. Scalability is the capability of a system, network, or process

    to handle a growing amount of work, or its potential to be enlarged to accommodate that growth. – Wikipedia
  2. class TextCard extends React.Component { static contextTypes = { metatypes:

    React.PropTypes.object, }; render() { const {cardData} = this.props; const {metatypes} = this.context; return ( <div> The following is either editable or displayed: <metatypes.text value={cardData.text} onChange={this.props.onChange} /> </div> ) } } function selectCardComponent(cardData) { switch (cardData.type) { case 'text': return TextCard; default: throw new Error(`Invalid card type ${cardData.type}`); } }
  3. class TextCard extends React.Component { static contextTypes = { metatypes:

    React.PropTypes.object, }; render() { const {cardData} = this.props; const {metatypes} = this.context; return ( <div> The following is either editable or displayed: <metatypes.text value={cardData.text} onChange={this.props.onChange} /> </div> ) } } function selectCardComponent(cardData) { switch (cardData.type) { case 'text': return TextCard; default: throw new Error(`Invalid card type ${cardData.type}`); } }
  4. const metatypesEdit = { text: class extends React.Component { render()

    { return <input type="text" {...this.props} />; } } } const metatypesView = { text: class extends React.Component { render() { return <span>{this.props.value}</span>; } } }
  5. class CardViewer extends React.Component { static childContextTypes = { metatypes:

    React.PropTypes.object }; getChildContext() { return {metatypes: metatypesView}; } render() { const {cardData} = this.props; const CardComponent = selectCardComponent(cardData); return <CardComponent cardData={cardData} /> } }
  6. class CardEditor extends React.Component { static childContextTypes = { metatypes:

    React.PropTypes.object }; getChildContext() { return {metatypes: metatypesEdit}; } render() { const {cardData} = this.props; const CardComponent = selectCardComponent(cardData); return <CardComponent cardData={cardData} /> } }
  7. function persistPostAction(post, callback = () => {}) { return {

    type: 'PERSIST_POST', post, callback }; } function *fetchPostsSaga(action) { const status = yield putPostAPI(action.post); yield put(persistPostCompleteAction(status)); yield call(action.callback, status); } class ComposePost extends React.Component { onClickSubmit() { const {dispatch} = this.props; const {post} = this.state; dispatch(persistPostAction(post, () => this.displaySuccessBanner())); } }
  8. class ViewPostPage extends React.Component { componentWillMount() { const {dispatch, postId}

    = this.props; dispatch(fetchPostAction(postId, () => this.logPageLoadComplete())); } }
  9. { byId: { 'a': userA, 'b': userB, 'c': userC, 'd':

    userD }, keyWindows: ['browseUsers', 'allManagers'], browseUsers: { ids: ['a', 'b', 'c'], isFetching: false, page: 1, totalPages: 10, next: '/users?page=2', last: '/users?page=10' }, allManagers: { ids: ['d', 'a'], isFetching: false } }
  10. function selectUserById(store, userId) { return store.users.byId[userId]; } function selectUsersByKeyWindow(store, keyWindow)

    { return store.users[keyWindow].ids.map(userId => selectUserById(store, userId)); }
  11. function fetchUsers({query}, keyWindow) { return { type: FETCH_USERS, query, keyWindow

    }; } function fetchManagers() { return fetchUsers({query: {isManager: true}}, 'allManager'); } function receiveEntities(entities, keyWindow) { return { type: RECEIVE_ENTITIES, entities, keyWindow }; }
  12. function reducer(state = defaultState, action) { switch(action.type) { case FETCH_USERS:

    return { ...state, keyWindows: uniq([...state.keyWindows, action.keyWindow]), [action.keyWindow]: { ...state[action.keyWindow], isFetching: true, query: action.query } }; case RECEIVE_ENTITIES: return { ...state, byId: { ...state.byId, ...action.entities.users.byId }, keyWindows: uniq([...state.keyWindows, action.keyWindow]), [action.keyWindow]: { ...state[action.keyWindow], isFetching: false, ids: action.entities.users.ids } }; } }
  13. function reducer(state = defaultState, action) { switch(action.type) { case FETCH_USERS:

    return { ...state, keyWindows: uniq([...state.keyWindows, action.keyWindow]), [action.keyWindow]: { ...state[action.keyWindow], isFetching: true, query: action.query } }; case RECEIVE_ENTITIES: return { ...state, byId: { ...state.byId, ...action.entities.users.byId }, keyWindows: uniq([...state.keyWindows, action.keyWindow]), [action.keyWindow]: { ...state[action.keyWindow], isFetching: false, ids: action.entities.users.ids } }; } }
  14. function reducer(state = defaultState, action) { switch(action.type) { case UPDATE_USER:

    return { ...state, draftsById: { ...state.draftsById, [action.user.id]: action.user } }; case RECEIVE_ENTITIES: return { ...state, byId: { ...state.byId, ...action.entities.users.byId }, draftsById: { ...omit(state.draftsById, action.entities.users.byId) }, keyWindows: uniq([...state.keyWindows, action.keyWindow]), [action.keyWindow]: { ...state[action.keyWindow], isFetching: false, ids: action.entities.users.ids } }; } }
  15. function reducer(state = defaultState, action) { switch(action.type) { case UPDATE_USER:

    return { ...state, draftsById: { ...state.draftsById, [action.user.id]: action.user } }; case RECEIVE_ENTITIES: return { ...state, byId: { ...state.byId, ...action.entities.users.byId }, draftsById: { ...omit(state.draftsById, action.entities.users.byId) }, keyWindows: uniq([...state.keyWindows, action.keyWindow]), [action.keyWindow]: { ...state[action.keyWindow], isFetching: false, ids: action.entities.users.ids } }; } }
  16. function reducer(state = defaultState, action) { switch(action.type) { case UNDO_UPDATE_USER:

    return { ...state, draftsById: { ...omit(state.draftsById, action.user.id), } }; } }
  17. class Routes extends React.Component { render() { return ( <Switch>

    <Route exact path="/" component={require(‘../home').default} /> <Route path="/admin" component={lazy(require(‘bundle-loader?lazy&name=admin!../admin’))} /> <Route component={PageNotFound} /> </Switch> ); } }
  18. const lazy = loader => class extends React.Component { componentWillMount()

    { loader(mod => this.setState({ Component: mod.default ? mod.default : mod }) ); } render() { const { Component } = this.state; if (Component !== null) { return <Component {...this.props} />; } else { return <div>Is Loading!</div>; } } };
  19. Pre: Clear homebrew & yarn caches 1. Reinstall node &

    yarn via brew 2. Clone repo 3. Run yarn install 4. Run production build 1. Compile & Minify CSS 2. Compile Server via Babel 3. Compile, Minify, & Gzip via Webpack 190.64s ~3 min
  20. Team 1 Team 2 Merge Feature A Merge Feature B

    Deploy Deploy OMG ROLLBACK DEPLOY!!! Merge Feature C Merge Bugfix for A Deploy Deploy BLOCKED!!! Deploy
  21. Team 1 Team 2 Merge Feature A Merge Feature B

    Deploy Deploy Rollout Flag A Rollout Flag B OMG ROLLBACK FLAG A!!! Merge Feature C Deploy Merge Bugfix for A Deploy Rollout Flag A Rollout Flag C
  22. Can you optimize your directory structure around team responsibilities? If

    teams are organized by “product domain”, Can you organize code around product domain?