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

React @ Scale

React @ Scale

Daniel Cousineau

May 04, 2019
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. onClick(e) { this.setState({val: e.target.value.toUpperCase()}); } // ...or... const Component =

    () => { const [val, setVal] = useState(''); return ( <input value={val} onChange={e => setVal(e.target.value.toUpperCase())} /> ); }
  3. onClick() { this.setState((prevState, currentProps) => { return { show: !prevState.show,

    }; }); } // ...or... const Component = () => { const [show, setShow] = useState(false); return ( <a onClick={setShow(prevShow => !prevShow)}>Hello</a> ); }
  4. onClick(e) { this.setState({value: e.target.value}, () => { this.props.onChange(this.state.value); }); }

    // ... or ... const Component = ({ onChange }) => { const [val, setVal] = useState(''); useEffect(() => { onChange(val); }, [val]); return ( <input value={val} onChange={e => setVal(e.target.value)} /> ); }
  5. const lazyComponent = (lazyImport, fallback = null) => { const

    LazyComponent = React.lazy(lazyImport); return props => ( <React.Suspense fallback={fallback}> <LazyComponent {...props} /> </React.Suspense> ); }; const AsyncComponent = lazyComponent(() => import('./async.js')); const Component = ({ show }) => { return ( <div> {show && <AsyncComponent any={props} you={want} />} </div> ); }
  6. • API Responses (“shared cache”) • Data required at deeply

    nested levels • Truly global layout state (menu open, etc)
  7. const mapStateToProps = (store) => { const currentUserId = selectCurrentUserId(store);

    return { user: selectUserById(store, currentUserId), }; }; export default connect(mapStateToProps)(YourComponent);
  8. { data: { 'a': userA, 'b': userB, 'c': userC, 'd':

    userD }, namespaces: ['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 } }
  9. function selectUserById(store, userId) { return store.users.data[userId]; } function selectUsersByNamespace(store, ns)

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

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

    return { ...state, namespaces: uniq([...state.namespaces, action.ns]), [action.ns]: { ...state[action.ns], isFetching: true, query: action.query } }; case RECEIVE_ENTITIES: return { ...state, data: { ...state.data, ...action.entities.users.data }, namespaces: uniq([...state.namespaces, action.ns]), [action.ns]: { ...state[action.ns], isFetching: false, ids: action.entities.users.ids } }; } }
  12. function reducer(state = defaultState, action) { switch(action.type) { case FETCH_USERS:

    return { ...state, namespaces: uniq([...state.namespaces, action.ns]), [action.ns]: { ...state[action.ns], isFetching: true, query: action.query } }; case RECEIVE_ENTITIES: return { ...state, data: { ...state.data, ...action.entities.users.data }, namespaces: uniq([...state.namespaces, action.ns]), [action.ns]: { ...state[action.ns], isFetching: false, ids: action.entities.users.ids } }; } }
  13. 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, data: { ...state.data, ...action.entities.users.data }, draftsById: { ...omit(state.draftsById, keys(action.entities.users.data)) }, namespaces: uniq([...state.namespaces, action.ns]), [action.ns]: { ...state[action.ns], 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, data: { ...state.data, ...action.entities.users.data }, draftsById: { ...omit(state.draftsById, keys(action.entities.users.data)) }, namespaces: uniq([...state.namespaces, action.ns]), [action.ns]: { ...state[action.ns], isFetching: false, ids: action.entities.users.ids } }; } }
  15. function reducer(state = defaultState, action) { switch(action.type) { case UNDO_UPDATE_USER:

    return { ...state, draftsById: { ...omit(state.draftsById, action.user.id), } }; } }
  16. const path = require('path'); module.exports = { mode: 'development', entry:

    './index.js', output: { path: path.resolve('./dist'), filename: 'index.bundle.js' } }; webpack.config.js
  17. Hash: 27b16bd3779b96e0ae26 Version: webpack 4.30.0 Time: 51ms Built at: 05/01/2019

    6:31:57 PM Asset Size Chunks Chunk Names 0.index.bundle.js 325 bytes 0 [emitted] index.bundle.js 8.21 KiB main [emitted] main Entrypoint main = index.bundle.js [./async.js] 33 bytes {0} [built] [./index.js] 36 bytes {main} [built]
  18. const path = require('path'); const webpack = require('webpack'); module.exports =

    { mode: 'development', entry: { index: [ require.resolve('webpack-dev-server/client') + '?http://localhost:3002/', require.resolve('webpack/hot/dev-server'), './index.js', ] }, output: { path: path.resolve('./dist'), publicPath: '/dist/', filename: 'index.bundle.js' }, plugins: [ new webpack.HotModuleReplacementPlugin(), ] }; webpack.config.js
  19. > webpack-dev-server --disable-host-check --progress --color --port 3002 --inline=false ℹ 「wds」:

    Project is running at http://localhost:3002/webpack-dev-server/ ℹ 「wds」: webpack output is served from /dist/ ℹ 「wdm」: Hash: 39da216c3734d73fa85c Version: webpack 4.30.0 Time: 278ms Built at: 05/01/2019 6:37:44 PM Asset Size Chunks Chunk Names 0.index.bundle.js 325 bytes 0 [emitted] index.bundle.js 382 KiB index [emitted] index Entrypoint index = index.bundle.js [0] multi (webpack)-dev-server/client?http://localhost:3002/ (webpack)/hot/dev-server.js ./index.js 52 bytes {index} [built] [./async.js] 33 bytes {0} [built] [./index.js] 36 bytes {index} [built] [./node_modules/loglevel/lib/loglevel.js] 7.68 KiB {index} [built] [./node_modules/querystring-es3/index.js] 127 bytes {index} [built] [./node_modules/url/url.js] 22.8 KiB {index} [built] [./node_modules/webpack-dev-server/client/index.js?http://localhost:3002/] (webpack)-dev-server/client? http://localhost:3002/ 8.26 KiB {index} [built] [./node_modules/webpack-dev-server/client/overlay.js] (webpack)-dev-server/client/overlay.js 3.59 KiB {index} [built] [./node_modules/webpack-dev-server/client/socket.js] (webpack)-dev-server/client/socket.js 1.05 KiB {index} [built]
  20. const express = require('express'); const app = express(); //... app.use(

    /(^\/dist|^\/dist\/.*\.hot-update\.js(on)?|__webpack_hmr)/i, streamingHttpProxy('http://localhost:3002', { forwardPath: req =># req.originalUrl }) ); Proxy your dev server Or use https://github.com/jantimon/html-webpack-plugin
  21. Pre: Clear homebrew & npm caches 1. Clone repo 2.

    Run npm install 3. Run production build 1. Compile & Minify CSS 2. Compile, Minify, & Gzip via Webpack 138.42s ~2 min
  22. 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
  23. 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
  24. Can you optimize your directory structure around team responsibilities? If

    teams are organized by “product domain”, Can you organize code around product domain?
  25. You will not get it perfect the first time. Optimize

    your code for easier refactoring.