SlideShare a Scribd company logo
React+Redux @ Scale
InfoQ.com: News & Community Site
• 750,000 unique visitors/month
• Published in 4 languages (English, Chinese, Japanese and Brazilian
Portuguese)
• Post content from our QCon conferences
• News 15-20 / week
• Articles 3-4 / week
• Presentations (videos) 12-15 / week
• Interviews 2-3 / week
• Books 1 / month
Watch the video with slide
synchronization on InfoQ.com!
https://www.infoq.com/presentations/
react-redux-scale
Presented at QCon New York
www.qconnewyork.com
Purpose of QCon
- to empower software development by facilitating the spread of
knowledge and innovation
Strategy
- practitioner-driven conference designed for YOU: influencers of
change and innovation in your teams
- speakers and topics driving the evolution and innovation
- connecting and catalyzing the influencers and innovators
Highlights
- attended by more than 12,000 delegates since 2007
- held in 9 cities worldwide
@dcousineau
React+Redux at Scale
React+Redux at Scale
React+Redux at Scale
React+Redux at Scale
Rules
React+Redux at Scale
“Rules”
React+Redux at Scale
React+Redux at Scale
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
Part 1: React
Rule: Components should be stateless
Reality: State is the enemy, but also inevitable
onClick(e) {
const value = e.target.value;
const formatted = value.toUpperCase();
this.setState({value: formatted});
}
onClick() {
this.setState((previousState, currentProps) => {
return {
show: !previousState.show,
};
});
}
onClick(e) {
this.setState({value: e.target.value});
this.props.onChange(this.state.value);
}
onClick(e) {
this.setState({value: e.target.value}, () => {
this.props.onChange(this.state.value);
});
}
Rule: Don’t use Context, it hides complexity
Reality: Sometimes complexity should be hidden
React+Redux at Scale
React+Redux at Scale
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}`);
}
}
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}`);
}
}
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>;
}
}
}
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} />
}
}
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} />
}
}
Part 2: Redux
Rule: “Single source of truth” means all state in the store
Reality: You can have multiple “single sources”
this.state.checked = true;
this.props.checked = true; this.props.checked = true; this.props.checked = true;
this.state.checked = true;
this.props.checked = true; this.props.checked = true; this.props.checked = true;
this.props.checked = true; checked: true
connect()();
window.location.*
Rule: Side effects should happen outside the Redux cycle
Reality: This doesn’t mean you can’t have callbacks
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()));
}
}
class ViewPostPage extends React.Component {
componentWillMount() {
const {dispatch, postId} = this.props;
dispatch(fetchPostAction(postId, () => this.logPageLoadComplete()));
}
}
Rule: Redux stores must be normalized for performance
Reality: You must normalize to reduce complexity
https://medium.com/@dcousineau/advanced-redux-entity-normalization-f5f1fe2aefc5
{
byId: {
...entities
},
keyWindows: [`${keyWindowName}`],
[keyWindowName]: {
ids: ['id0', ..., 'idN'],
...meta
}
}
{
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
}
}
function selectUserById(store, userId) {
return store.users.byId[userId];
}
function selectUsersByKeyWindow(store, keyWindow) {
return store.users[keyWindow].ids.map(userId => selectUserById(store, userId));
}
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
};
}
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
}
};
}
}
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
}
};
}
}
function selectUsersAreFetching(store, keyWindow) {
return !!store.users[keyWindow].isFetching;
}
function selectManagersAreFetching(store) {
return selectUsersAreFetching(store, 'allManagers');
}
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
}
};
}
}
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
}
};
}
}
function selectUserById(store, userId) {
return store.users.draftsById[userId] || store.users.byId[userId];
}
function reducer(state = defaultState, action) {
switch(action.type) {
case UNDO_UPDATE_USER:
return {
...state,
draftsById: {
...omit(state.draftsById, action.user.id),
}
};
}
}
Part 3: Scale
Rule: Keep dependencies low to keep the application fast
Reality: Use bundling to increase PERCEIVED performance
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>
);
}
}
require('bundle-loader?lazy&name=admin!../admin’)
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>;
}
}
};
React+Redux at Scale
Rule: Render up-to-date data
Reality: If you got something render it, update it later
React+Redux at Scale
React+Redux at Scale
React+Redux at Scale
React+Redux at Scale
React+Redux at Scale
React+Redux at Scale
Epilog: Scale?
Rule: Scale is bytes served, users concurrent
Reality: Scale is responding to bytes served and users concurrent
How fast can you deploy?
React+Redux at Scale
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
<Feature name="new-feature" fallback={<OldFeatureComponent />}>
<NewFeatureComponent />
</Feature>
React+Redux at Scale
Team 1
Team 2
MergeFeatureA
MergeFeatureB
Deploy
Deploy
OMG
ROLLBACK
DEPLOY!!!
MergeFeatureC
MergeBugfixforA
Deploy
DeployBLOCKED!!!
Deploy
Team 1
Team 2
MergeFeatureA
MergeFeatureB
Deploy
Deploy
RolloutFlagARolloutFlagBOMG
ROLLBACK
FLAG
A!!!
MergeFeatureC
Deploy
MergeBugfixforA
Deploy
RolloutFlagA
RolloutFlagC
Can you optimize your directory structure around team responsibilities?
If teams are organized by “product domain”,
Can you organize code around product domain?
Final Thoughts
Strict rules rarely 100% apply to your application.
Remembering the purpose behind the rules is valuable.
Code behavior should be predictable and intuitable.
Be realistic about the problem you’re actually solving.
You will not get it perfect the first time.
Optimize your processes for refactoring.
Questions?
Watch the video with slide synchronization on
InfoQ.com!
https://www.infoq.com/presentations/react-
redux-scale

More Related Content

PDF
Agile Data concept introduction
PDF
Advanced Redux architecture - WHAT/WHEN/WHY/HOW
PDF
Redux essentials
PDF
Intro to Redux | DreamLab Academy #3
PDF
Understanding redux
PPTX
Maintaining sanity in a large redux app
PDF
Evan Schultz - Angular Summit - 2016
PDF
Redux js
Agile Data concept introduction
Advanced Redux architecture - WHAT/WHEN/WHY/HOW
Redux essentials
Intro to Redux | DreamLab Academy #3
Understanding redux
Maintaining sanity in a large redux app
Evan Schultz - Angular Summit - 2016
Redux js

Similar to React+Redux at Scale (20)

PPTX
Redux training
PPTX
Introduction to react and redux
PDF
Redux State Management System A Comprehensive Review
PDF
React Redux Tutorial | Redux Tutorial for Beginners | React Redux Training | ...
PDF
Let's discover React and Redux with TypeScript
PDF
Let's Redux!
PPTX
React/Redux
PDF
React lecture
PPTX
Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
PDF
Building React Applications with Redux
PDF
Introduction to Redux
PPTX
Getting started with Redux js
PPT
The Redux Introduction in react and applications .
PDF
ReactRedux.pdf
PDF
2018 02-22 React, Redux & Building Applications that Scale | Redux
PPTX
Redux Like Us
PDF
Egghead redux-cheat-sheet-3-2-1
PDF
Materi Modern React Redux Power Point.pdf
PDF
10 tips for Redux at scale
PDF
Redux at scale
Redux training
Introduction to react and redux
Redux State Management System A Comprehensive Review
React Redux Tutorial | Redux Tutorial for Beginners | React Redux Training | ...
Let's discover React and Redux with TypeScript
Let's Redux!
React/Redux
React lecture
Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Building React Applications with Redux
Introduction to Redux
Getting started with Redux js
The Redux Introduction in react and applications .
ReactRedux.pdf
2018 02-22 React, Redux & Building Applications that Scale | Redux
Redux Like Us
Egghead redux-cheat-sheet-3-2-1
Materi Modern React Redux Power Point.pdf
10 tips for Redux at scale
Redux at scale
Ad

More from C4Media (20)

PDF
Streaming a Million Likes/Second: Real-Time Interactions on Live Video
PDF
Next Generation Client APIs in Envoy Mobile
PDF
Software Teams and Teamwork Trends Report Q1 2020
PDF
Understand the Trade-offs Using Compilers for Java Applications
PDF
Kafka Needs No Keeper
PDF
High Performing Teams Act Like Owners
PDF
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
PDF
Service Meshes- The Ultimate Guide
PDF
Shifting Left with Cloud Native CI/CD
PDF
CI/CD for Machine Learning
PDF
Fault Tolerance at Speed
PDF
Architectures That Scale Deep - Regaining Control in Deep Systems
PDF
ML in the Browser: Interactive Experiences with Tensorflow.js
PDF
Build Your Own WebAssembly Compiler
PDF
User & Device Identity for Microservices @ Netflix Scale
PDF
Scaling Patterns for Netflix's Edge
PDF
Make Your Electron App Feel at Home Everywhere
PDF
The Talk You've Been Await-ing For
PDF
Future of Data Engineering
PDF
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
Streaming a Million Likes/Second: Real-Time Interactions on Live Video
Next Generation Client APIs in Envoy Mobile
Software Teams and Teamwork Trends Report Q1 2020
Understand the Trade-offs Using Compilers for Java Applications
Kafka Needs No Keeper
High Performing Teams Act Like Owners
Does Java Need Inline Types? What Project Valhalla Can Bring to Java
Service Meshes- The Ultimate Guide
Shifting Left with Cloud Native CI/CD
CI/CD for Machine Learning
Fault Tolerance at Speed
Architectures That Scale Deep - Regaining Control in Deep Systems
ML in the Browser: Interactive Experiences with Tensorflow.js
Build Your Own WebAssembly Compiler
User & Device Identity for Microservices @ Netflix Scale
Scaling Patterns for Netflix's Edge
Make Your Electron App Feel at Home Everywhere
The Talk You've Been Await-ing For
Future of Data Engineering
Automated Testing for Terraform, Docker, Packer, Kubernetes, and More
Ad

Recently uploaded (20)

PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Electronic commerce courselecture one. Pdf
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Approach and Philosophy of On baking technology
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
Cloud computing and distributed systems.
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Advanced methodologies resolving dimensionality complications for autism neur...
NewMind AI Weekly Chronicles - August'25 Week I
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Electronic commerce courselecture one. Pdf
MIND Revenue Release Quarter 2 2025 Press Release
Approach and Philosophy of On baking technology
Building Integrated photovoltaic BIPV_UPV.pdf
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Mobile App Security Testing_ A Comprehensive Guide.pdf
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
“AI and Expert System Decision Support & Business Intelligence Systems”
Cloud computing and distributed systems.
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
20250228 LYD VKU AI Blended-Learning.pptx
Network Security Unit 5.pdf for BCA BBA.
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows

React+Redux at Scale

  • 2. InfoQ.com: News & Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month Watch the video with slide synchronization on InfoQ.com! https://www.infoq.com/presentations/ react-redux-scale
  • 3. Presented at QCon New York www.qconnewyork.com Purpose of QCon - to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide
  • 14. 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
  • 16. Rule: Components should be stateless
  • 17. Reality: State is the enemy, but also inevitable
  • 18. onClick(e) { const value = e.target.value; const formatted = value.toUpperCase(); this.setState({value: formatted}); }
  • 19. onClick() { this.setState((previousState, currentProps) => { return { show: !previousState.show, }; }); }
  • 21. onClick(e) { this.setState({value: e.target.value}, () => { this.props.onChange(this.state.value); }); }
  • 22. Rule: Don’t use Context, it hides complexity
  • 23. Reality: Sometimes complexity should be hidden
  • 26. 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}`); } }
  • 27. 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}`); } }
  • 28. 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>; } } }
  • 29. 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} /> } }
  • 30. 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} /> } }
  • 32. Rule: “Single source of truth” means all state in the store
  • 33. Reality: You can have multiple “single sources”
  • 35. this.props.checked = true; this.props.checked = true; this.props.checked = true; this.state.checked = true;
  • 36. this.props.checked = true; this.props.checked = true; this.props.checked = true; this.props.checked = true; checked: true connect()();
  • 38. Rule: Side effects should happen outside the Redux cycle
  • 39. Reality: This doesn’t mean you can’t have callbacks
  • 40. 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())); } }
  • 41. class ViewPostPage extends React.Component { componentWillMount() { const {dispatch, postId} = this.props; dispatch(fetchPostAction(postId, () => this.logPageLoadComplete())); } }
  • 42. Rule: Redux stores must be normalized for performance
  • 43. Reality: You must normalize to reduce complexity
  • 46. { 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 } }
  • 47. function selectUserById(store, userId) { return store.users.byId[userId]; } function selectUsersByKeyWindow(store, keyWindow) { return store.users[keyWindow].ids.map(userId => selectUserById(store, userId)); }
  • 48. 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 }; }
  • 49. 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 } }; } }
  • 50. 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 } }; } }
  • 51. function selectUsersAreFetching(store, keyWindow) { return !!store.users[keyWindow].isFetching; } function selectManagersAreFetching(store) { return selectUsersAreFetching(store, 'allManagers'); }
  • 52. 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 } }; } }
  • 53. 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 } }; } }
  • 54. function selectUserById(store, userId) { return store.users.draftsById[userId] || store.users.byId[userId]; }
  • 55. function reducer(state = defaultState, action) { switch(action.type) { case UNDO_UPDATE_USER: return { ...state, draftsById: { ...omit(state.draftsById, action.user.id), } }; } }
  • 57. Rule: Keep dependencies low to keep the application fast
  • 58. Reality: Use bundling to increase PERCEIVED performance
  • 59. 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> ); } }
  • 61. 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>; } } };
  • 64. Reality: If you got something render it, update it later
  • 72. Rule: Scale is bytes served, users concurrent
  • 73. Reality: Scale is responding to bytes served and users concurrent
  • 74. How fast can you deploy?
  • 76. 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
  • 77. <Feature name="new-feature" fallback={<OldFeatureComponent />}> <NewFeatureComponent /> </Feature>
  • 81. Can you optimize your directory structure around team responsibilities? If teams are organized by “product domain”, Can you organize code around product domain?
  • 83. Strict rules rarely 100% apply to your application. Remembering the purpose behind the rules is valuable.
  • 84. Code behavior should be predictable and intuitable. Be realistic about the problem you’re actually solving.
  • 85. You will not get it perfect the first time. Optimize your processes for refactoring.
  • 87. Watch the video with slide synchronization on InfoQ.com! https://www.infoq.com/presentations/react- redux-scale