SlideShare ist ein Scribd-Unternehmen logo
1 von 51
React Responsively, Render Responsibly Yoav Niran
React Responsively,
Render Responsibly
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
A little about me
Yoav Niran
Senior front-end developer @ Cloudinary
The Media Full Stack
Heavy-duty image & video platform
@poeticGeek
React Responsively, Render Responsibly Yoav Niran
The crime scene
React Responsively, Render Responsibly Yoav Niran
The crime scene
App
SelectionView
footer
Header
PhotosGrid
PhotoItem PhotoItem PhotoItem
React Responsively, Render Responsibly Yoav Niran
DEM
React Responsively, Render Responsibly Yoav Niran
FPS Indicator
CPU Chart
Main flame-graph
Frames
Summary
React Responsively, Render Responsibly Yoav Niran
User Timing
React Responsively, Render Responsibly Yoav Niran
The road to performance
572.09 ms 13.18 ms
React Responsively, Render Responsibly Yoav Niran
The road to performance
1. Data go low
2. Time to leave the nest
3. Spread the love props
4. Just Memoize It TM
5. Open a Window
React Responsively, Render Responsibly Yoav Niran
State state = Immutable({
photos: [
{
id: "aaa",
url: "https://..."
width: 2000,
height: 2500,
selected: true,
},
{
id: "bbb",
url: "https://..."
width: 2000,
height: 2500,
selected: false,
},
{
id: "ccc",
url: "https://..."
width: 2000,
height: 2500,
selected: false,
},
...
React Responsively, Render Responsibly Yoav Niran
Clue #1 – You don’t need that data
class PhotosGrid extends Component {
render() {
...
return (
<div className={cx(styles.container, "...")}>
...
{photos.map((p) => (
<PhotoItem
key={p.url}
item={p}/>
))}
</div>
);}}
export default connect(
(state) => ({
fetchStatus: selectFetchStatus(state),
photos: selectPhotos(state),
}),
...
PhotosGrid/PhotosGrid.js
React Responsively, Render Responsibly Yoav Niran
Fix #1 – Data go low
class PhotosGrid extends Component {
render() {
...
return (
<div className={cx(styles.container, "...")}>
...
{photos.map((p) => (
<PhotoItem
key={p.url}
item={p}/>
))}
</div>
);}}
export default connect(
(state) => ({
fetchStatus: selectFetchStatus(state),
photos: selectPhotos(state),
}),
...
class PhotosGrid extends Component {
render() {
...
return (
<div className={cx(styles.container,
"...")}>
...
{photos.map((id) => (
<PhotoItem
key={id}
id={id}/>
))}
</div>
);}}
export default connect(
(state) => ({
fetchStatus: selectFetchStatus(state),
photos: selectPhotoIds(state),
}),
...
PhotosGrid/PhotosGrid.js
React Responsively, Render Responsibly Yoav Niran
Fix #1 - Data go low
const PhotoItem = (props) => {
const {id, filename, width, height, selected,
price} = props.item,
horizontal = props.horizontal;
return (
<article>
...
</article>
);
};
export default connect(
null,
bindActions,
)(RenderCounter(PhotoItem));
const photoItemSelector = (state, props) => {
const photo = state.photos
.find((p) => p.public_id === props.id);
return {
item: takePhotoProps(photo),
};
};
export default connect(
photoItemSelector,
bindActions,
)(RenderCounter(PhotoItem));
PhotoItem/PhotoItem.js
React Responsively, Render Responsibly Yoav Niran
DEM
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
The Journey
1. Data go low
2. Time to leave the nest
3. Spread the love props
4. Just Memoize It TM
5. Open a Window
React Responsively, Render Responsibly Yoav Niran
State state = Immutable({
photos: [
{
id: "aaa",
url: "https://..."
width: 2000,
height: 2500,
selected: true,
},
{
id: "bbb",
url: "https://..."
width: 2000,
height: 2500,
selected: false,
},
{
id: "ccc",
url: "https://..."
width: 2000,
height: 2500,
selected: false,
},
...
React Responsively, Render Responsibly Yoav Niran
Clue #2 – Deep in the nest
export default createReducer(initialState, {
…
[TYPES.SET_SELECTED_PHOTO]: (state, {payload}) => {
const index = getPhotoIndex(state.photos, payload.id);
state = state.setIn(["photos", index, "selected"],
payload.selected);
...
return state;
},
const initialState = Immutable({
...
photos: [],
...
});
store/reducers.js
React Responsively, Render Responsibly Yoav Niran
Fix #2 – Time to leave the nest
export default
createReducer(initialState, {
…
[TYPES.SET_SELECTED_PHOTO]:
(state, {payload}) => {
const index =
getPhotoIndex(state.photos,
payload.id);
state = state.setIn(["photos",
index, "selected"],
payload.selected);
...
return state;
},
const initialState = Immutable({
...
photos: [],
...
});
[TYPES.SET_SELECTED_PHOTO]: (state, {payload}) => {
const selectedIndex = state.selected.indexOf(payload.id);
if (!~selectedIndex) {
if (payload.selected) {
state = state.set("selected",
state.selected.concat(payload.id));
}
}
else if (!payload.selected) {
state = state.set("selected",
state.selected.filter((s) => s !==
payload.id));
}
...
},
const initialState = Immutable({
...
photos: [],
selected: [],
});
store/reducers.js
React Responsively, Render Responsibly Yoav Niran
DEM
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
The Journey
1. Data go low
2. Time to leave the nest
3. Spread the love props
4. Just Memoize It TM
5. Open a Window
React Responsively, Render Responsibly Yoav Niran
Clue #3 - Pure & Shallow Equal
const getPhotoItemProps = (photo, selected) => {
return {
item: takePhotoProps({...photo, selected}),
};
};
const photoItemSelector = (state, props) => {
const photo = state.photos
.find((p) => p.public_id === props.id);
const selected = !!~state.selected.indexOf(props.id);
return getPhotoItemProps(photo, selected);
};
export default connect(
photoItemSelector,
bindActions,
)(RenderCounter(PhotoItem));
PhotoItem/PhotoItem.js
React Responsively, Render Responsibly Yoav Niran
Fix #3 - Spread the love props
const getPhotoItemProps = (photo, selected) => {
return {
item: takePhotoProps({...photo, selected}),
};
};
const photoItemSelector = (state, props) => {
const photo = state.photos
.find((p) => p.public_id === props.id);
const selected = !!~state.selected.indexOf(props.id);
return getPhotoItemProps(photo, selected);
};
export default connect(
photoItemSelector,
bindActions,
)(RenderCounter(PhotoItem));
PhotoItem/PhotoItem.js
const getPhotoItemProps = (photo, selected) => {
return {
...takePhotoProps({...photo, selected}),
};
};
const photoItemSelector = (state, props) => {
const photo = state.photos
.find((p) => p.public_id === props.id);
const selected =
!!~state.selected.indexOf(props.id);
return getPhotoItemProps(photo, selected);
};
export default connect(
photoItemSelector,
bindActions,
)(RenderCounter(PhotoItem));
React Responsively, Render Responsibly Yoav Niran
DEM
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
Yay?
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
Memoization
“...memoization or memoisation is an optimization technique used
primarily to speed up computer programs by storing the results of
expensive function calls and returning the cached result when the same
inputs occur again…”
https://en.wikipedia.org/wiki/Memoization
Memoization
React Responsively, Render Responsibly Yoav Niran
Clue #4 - Memoization
React Responsively, Render Responsibly Yoav Niran
The Journey
1. Data go low
2. Time to leave the nest
3. Spread the love props
4. Just Memoize It TM
5. Open a Window
React Responsively, Render Responsibly Yoav Niran
Fix #4 – Just memoize it
PhotoItem/PhotoItem.selectors.js
import {createSelector} from "reselect";
import {takePhotoProps} from "../../selectors";
const getPhoto = (state, props) => {
return state.photos
.find((p) => p.public_id === props.id);
};
const selectIsSelectedById = (state, props) =>
!!~state.selected.indexOf(props.id);
export const getPhotoByIdSelector = () =>
createSelector(
[
getPhoto,
selectIsSelectedById
],
(photo, selected) =>
({...takePhotoProps(photo), selected}),
);
TM
React Responsively, Render Responsibly Yoav Niran
const getPhotoItemProps = (photo, selected) => {
return {
...takePhotoProps({...photo, selected}),
};
};
const photoItemSelector = (state, props) => {
const photo = state.photos
.find((p) => p.public_id === props.id);
const selected = !!~state.selected.indexOf(props.id);
return getPhotoItemProps(photo, selected);
};
export default connect(
photoItemSelector,
bindActions,
)(RenderCounter(PhotoItem));
PhotoItem/PhotoItem.js import {getPhotoByIdSelector} from "./PhotoItem.selectors
const PhotoItem = (props) => {
const {id, …, selected, price} = props.item,
horizontal = props.horizontal;
return (
<article>
...
</article>
);
};
export default connect(
() => {
const photoSelector = getPhotoByIdSelector();
return (state, props) => ({
...photoSelector(state, props),
});
},
bindActions,
)(RenderCounter(PhotoItem));
Fix #4 – Just memoize it TM
React Responsively, Render Responsibly Yoav Niran
DEM
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
Are we there yet?
39.35 ms
React Responsively, Render Responsibly Yoav Niran
DEM
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
The Journey
1. Data go low
2. Time to leave the nest
3. Spread the love props
4. Just Memoize It TM
5. Open a Window
React Responsively, Render Responsibly Yoav Niran
Clue #5 – Letting it all out
class PhotosGrid extends Component {
render() {
...
return (
<div className={cx(styles.container, "...")}>
...
{photos.map((id) => (
<PhotoItem
key={id}
id={id}/>
))}
</div>
);}}
...
PhotosGrid/PhotosGrid.js
React Responsively, Render Responsibly Yoav Niran
Fix #5 – Open a window
https://bvaughn.github.io/forward-js-2017
React Responsively, Render Responsibly Yoav Niran
Fix #5 – Open a window
https://react-window.now.sh
https://github.com/bvaughn/react-window
React Responsively, Render Responsibly Yoav Niran
Fix #5 – Open a window
class PhotosGrid extends Component {
render() {
...
return (
<div
className={cx(styles.container, "...")}>
...
{photos.map((id) => (
<PhotoItem
key={id}
id={id}/>
))}
</div>
);}}
...
PhotosGrid/PhotosGrid.js import {FixedSizeGrid as Grid} from "react-window";
renderItems() {
const {height, width, photos} = this.props;
const colCount = Math.floor(width / 250),
rowCount = photos.length / colCount;
return <Grid
ref={this.gridRef}
columnCount={colCount}
columnWidth={250}
height={height}
rowCount={(rowCount + 1)}
rowHeight={270}
width={width}
onScroll={({scrollTop}) => {
this.gridScrollTop = scrollTop;
}}>
{({columnIndex, rowIndex, style}) =>
<PhotoItem index={getItemIndex(columnIndex,
rowIndex, colCount)}
style={style}/>}
</Grid>
}
…
React Responsively, Render Responsibly Yoav Niran
DEM
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
DEM
React Responsively, Render Responsibly Yoav Niran
React Responsively, Render Responsibly Yoav Niran
The road to performance
1. Data go low
2. Time to leave the nest
3. Spread the love props
4. Just Memoize It TM
5. Open a Window
React Responsively, Render Responsibly Yoav Niran
We made it !
~600 ms ~10 ms
React Responsively, Render Responsibly Yoav Niran
Thank y u
https://github.com/yoavniran/react-performance-demo
@poeticGeek

Weitere ähnliche Inhalte

Ähnlich wie React Responsively, Render Responsibly

Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Robert DeLuca
 
Paging using Paging 3
Paging using Paging 3Paging using Paging 3
Paging using Paging 3Fandy Gotama
 
Oxygine 2 d objects,events,debug and resources
Oxygine 2 d objects,events,debug and resourcesOxygine 2 d objects,events,debug and resources
Oxygine 2 d objects,events,debug and resourcescorehard_by
 
A frame beginner lesson
A frame beginner lessonA frame beginner lesson
A frame beginner lessonDaosheng Mu
 
State management in android applications
State management in android applicationsState management in android applications
State management in android applicationsGabor Varadi
 
Angular2 & ngrx/store: Game of States
Angular2 & ngrx/store: Game of StatesAngular2 & ngrx/store: Game of States
Angular2 & ngrx/store: Game of StatesOren Farhi
 
"Use Component Drivers to Control Components in Tests", Roman Shevchuk
"Use Component Drivers to Control Components in Tests", Roman Shevchuk "Use Component Drivers to Control Components in Tests", Roman Shevchuk
"Use Component Drivers to Control Components in Tests", Roman Shevchuk Fwdays
 
Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)indeedeng
 
React 16: new features and beyond
React 16: new features and beyondReact 16: new features and beyond
React 16: new features and beyondArtjoker
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practicesClickky
 
Incremental Type Safety in React Apollo
Incremental Type Safety in React Apollo Incremental Type Safety in React Apollo
Incremental Type Safety in React Apollo Evans Hauser
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projectsIgnacio Martín
 
Design Patterns for Tablets and Smartphones
Design Patterns for Tablets and SmartphonesDesign Patterns for Tablets and Smartphones
Design Patterns for Tablets and SmartphonesMichael Galpin
 
Advanced React Component Patterns - ReactNext 2018
Advanced React Component Patterns - ReactNext 2018Advanced React Component Patterns - ReactNext 2018
Advanced React Component Patterns - ReactNext 2018Robert Herbst
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blinkInnovationM
 
첫 리액트 경험기
첫 리액트 경험기첫 리액트 경험기
첫 리액트 경험기석진 고
 

Ähnlich wie React Responsively, Render Responsibly (19)

Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
 
Paging using Paging 3
Paging using Paging 3Paging using Paging 3
Paging using Paging 3
 
Oxygine 2 d objects,events,debug and resources
Oxygine 2 d objects,events,debug and resourcesOxygine 2 d objects,events,debug and resources
Oxygine 2 d objects,events,debug and resources
 
A frame beginner lesson
A frame beginner lessonA frame beginner lesson
A frame beginner lesson
 
State management in android applications
State management in android applicationsState management in android applications
State management in android applications
 
Angular2 & ngrx/store: Game of States
Angular2 & ngrx/store: Game of StatesAngular2 & ngrx/store: Game of States
Angular2 & ngrx/store: Game of States
 
"Use Component Drivers to Control Components in Tests", Roman Shevchuk
"Use Component Drivers to Control Components in Tests", Roman Shevchuk "Use Component Drivers to Control Components in Tests", Roman Shevchuk
"Use Component Drivers to Control Components in Tests", Roman Shevchuk
 
Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
Indeed My Jobs: A case study in ReactJS and Redux (Meetup talk March 2016)
 
React 16: new features and beyond
React 16: new features and beyondReact 16: new features and beyond
React 16: new features and beyond
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
React + Redux. Best practices
React + Redux.  Best practicesReact + Redux.  Best practices
React + Redux. Best practices
 
Incremental Type Safety in React Apollo
Incremental Type Safety in React Apollo Incremental Type Safety in React Apollo
Incremental Type Safety in React Apollo
 
List adapter with multiple objects
List adapter with multiple objectsList adapter with multiple objects
List adapter with multiple objects
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
 
Design Patterns for Tablets and Smartphones
Design Patterns for Tablets and SmartphonesDesign Patterns for Tablets and Smartphones
Design Patterns for Tablets and Smartphones
 
Advanced React Component Patterns - ReactNext 2018
Advanced React Component Patterns - ReactNext 2018Advanced React Component Patterns - ReactNext 2018
Advanced React Component Patterns - ReactNext 2018
 
Capture image on eye blink
Capture image on eye blinkCapture image on eye blink
Capture image on eye blink
 
첫 리액트 경험기
첫 리액트 경험기첫 리액트 경험기
첫 리액트 경험기
 
Lithium Best
Lithium Best Lithium Best
Lithium Best
 

Kürzlich hochgeladen

Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingOpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingShane Coughlan
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identityteam-WIBU
 
Strategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsStrategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsJean Silva
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
VictoriaMetrics Anomaly Detection Updates: Q1 2024
VictoriaMetrics Anomaly Detection Updates: Q1 2024VictoriaMetrics Anomaly Detection Updates: Q1 2024
VictoriaMetrics Anomaly Detection Updates: Q1 2024VictoriaMetrics
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Rob Geurden
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
Best Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITBest Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITmanoharjgpsolutions
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profileakrivarotava
 
Osi security architecture in network.pptx
Osi security architecture in network.pptxOsi security architecture in network.pptx
Osi security architecture in network.pptxVinzoCenzo
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLionel Briand
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfkalichargn70th171
 
Effectively Troubleshoot 9 Types of OutOfMemoryError
Effectively Troubleshoot 9 Types of OutOfMemoryErrorEffectively Troubleshoot 9 Types of OutOfMemoryError
Effectively Troubleshoot 9 Types of OutOfMemoryErrorTier1 app
 
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfEnhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfRTS corp
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecturerahul_net
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf31events.com
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 

Kürzlich hochgeladen (20)

Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full RecordingOpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
OpenChain Education Work Group Monthly Meeting - 2024-04-10 - Full Recording
 
Post Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on IdentityPost Quantum Cryptography – The Impact on Identity
Post Quantum Cryptography – The Impact on Identity
 
Strategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero resultsStrategies for using alternative queries to mitigate zero results
Strategies for using alternative queries to mitigate zero results
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
VictoriaMetrics Anomaly Detection Updates: Q1 2024
VictoriaMetrics Anomaly Detection Updates: Q1 2024VictoriaMetrics Anomaly Detection Updates: Q1 2024
VictoriaMetrics Anomaly Detection Updates: Q1 2024
 
Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...Simplifying Microservices & Apps - The art of effortless development - Meetup...
Simplifying Microservices & Apps - The art of effortless development - Meetup...
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
Best Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh ITBest Angular 17 Classroom & Online training - Naresh IT
Best Angular 17 Classroom & Online training - Naresh IT
 
SoftTeco - Software Development Company Profile
SoftTeco - Software Development Company ProfileSoftTeco - Software Development Company Profile
SoftTeco - Software Development Company Profile
 
Osi security architecture in network.pptx
Osi security architecture in network.pptxOsi security architecture in network.pptx
Osi security architecture in network.pptx
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
Large Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and RepairLarge Language Models for Test Case Evolution and Repair
Large Language Models for Test Case Evolution and Repair
 
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdfExploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
Exploring Selenium_Appium Frameworks for Seamless Integration with HeadSpin.pdf
 
Effectively Troubleshoot 9 Types of OutOfMemoryError
Effectively Troubleshoot 9 Types of OutOfMemoryErrorEffectively Troubleshoot 9 Types of OutOfMemoryError
Effectively Troubleshoot 9 Types of OutOfMemoryError
 
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdfEnhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
Enhancing Supply Chain Visibility with Cargo Cloud Solutions.pdf
 
Understanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM ArchitectureUnderstanding Flamingo - DeepMind's VLM Architecture
Understanding Flamingo - DeepMind's VLM Architecture
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 

React Responsively, Render Responsibly

  • 1. React Responsively, Render Responsibly Yoav Niran React Responsively, Render Responsibly
  • 2. React Responsively, Render Responsibly Yoav Niran
  • 3. React Responsively, Render Responsibly Yoav Niran A little about me Yoav Niran Senior front-end developer @ Cloudinary The Media Full Stack Heavy-duty image & video platform @poeticGeek
  • 4. React Responsively, Render Responsibly Yoav Niran The crime scene
  • 5. React Responsively, Render Responsibly Yoav Niran The crime scene App SelectionView footer Header PhotosGrid PhotoItem PhotoItem PhotoItem
  • 6. React Responsively, Render Responsibly Yoav Niran DEM
  • 7. React Responsively, Render Responsibly Yoav Niran FPS Indicator CPU Chart Main flame-graph Frames Summary
  • 8. React Responsively, Render Responsibly Yoav Niran User Timing
  • 9. React Responsively, Render Responsibly Yoav Niran The road to performance 572.09 ms 13.18 ms
  • 10. React Responsively, Render Responsibly Yoav Niran The road to performance 1. Data go low 2. Time to leave the nest 3. Spread the love props 4. Just Memoize It TM 5. Open a Window
  • 11. React Responsively, Render Responsibly Yoav Niran State state = Immutable({ photos: [ { id: "aaa", url: "https://..." width: 2000, height: 2500, selected: true, }, { id: "bbb", url: "https://..." width: 2000, height: 2500, selected: false, }, { id: "ccc", url: "https://..." width: 2000, height: 2500, selected: false, }, ...
  • 12. React Responsively, Render Responsibly Yoav Niran Clue #1 – You don’t need that data class PhotosGrid extends Component { render() { ... return ( <div className={cx(styles.container, "...")}> ... {photos.map((p) => ( <PhotoItem key={p.url} item={p}/> ))} </div> );}} export default connect( (state) => ({ fetchStatus: selectFetchStatus(state), photos: selectPhotos(state), }), ... PhotosGrid/PhotosGrid.js
  • 13. React Responsively, Render Responsibly Yoav Niran Fix #1 – Data go low class PhotosGrid extends Component { render() { ... return ( <div className={cx(styles.container, "...")}> ... {photos.map((p) => ( <PhotoItem key={p.url} item={p}/> ))} </div> );}} export default connect( (state) => ({ fetchStatus: selectFetchStatus(state), photos: selectPhotos(state), }), ... class PhotosGrid extends Component { render() { ... return ( <div className={cx(styles.container, "...")}> ... {photos.map((id) => ( <PhotoItem key={id} id={id}/> ))} </div> );}} export default connect( (state) => ({ fetchStatus: selectFetchStatus(state), photos: selectPhotoIds(state), }), ... PhotosGrid/PhotosGrid.js
  • 14. React Responsively, Render Responsibly Yoav Niran Fix #1 - Data go low const PhotoItem = (props) => { const {id, filename, width, height, selected, price} = props.item, horizontal = props.horizontal; return ( <article> ... </article> ); }; export default connect( null, bindActions, )(RenderCounter(PhotoItem)); const photoItemSelector = (state, props) => { const photo = state.photos .find((p) => p.public_id === props.id); return { item: takePhotoProps(photo), }; }; export default connect( photoItemSelector, bindActions, )(RenderCounter(PhotoItem)); PhotoItem/PhotoItem.js
  • 15. React Responsively, Render Responsibly Yoav Niran DEM
  • 16. React Responsively, Render Responsibly Yoav Niran
  • 17. React Responsively, Render Responsibly Yoav Niran The Journey 1. Data go low 2. Time to leave the nest 3. Spread the love props 4. Just Memoize It TM 5. Open a Window
  • 18. React Responsively, Render Responsibly Yoav Niran State state = Immutable({ photos: [ { id: "aaa", url: "https://..." width: 2000, height: 2500, selected: true, }, { id: "bbb", url: "https://..." width: 2000, height: 2500, selected: false, }, { id: "ccc", url: "https://..." width: 2000, height: 2500, selected: false, }, ...
  • 19. React Responsively, Render Responsibly Yoav Niran Clue #2 – Deep in the nest export default createReducer(initialState, { … [TYPES.SET_SELECTED_PHOTO]: (state, {payload}) => { const index = getPhotoIndex(state.photos, payload.id); state = state.setIn(["photos", index, "selected"], payload.selected); ... return state; }, const initialState = Immutable({ ... photos: [], ... }); store/reducers.js
  • 20. React Responsively, Render Responsibly Yoav Niran Fix #2 – Time to leave the nest export default createReducer(initialState, { … [TYPES.SET_SELECTED_PHOTO]: (state, {payload}) => { const index = getPhotoIndex(state.photos, payload.id); state = state.setIn(["photos", index, "selected"], payload.selected); ... return state; }, const initialState = Immutable({ ... photos: [], ... }); [TYPES.SET_SELECTED_PHOTO]: (state, {payload}) => { const selectedIndex = state.selected.indexOf(payload.id); if (!~selectedIndex) { if (payload.selected) { state = state.set("selected", state.selected.concat(payload.id)); } } else if (!payload.selected) { state = state.set("selected", state.selected.filter((s) => s !== payload.id)); } ... }, const initialState = Immutable({ ... photos: [], selected: [], }); store/reducers.js
  • 21. React Responsively, Render Responsibly Yoav Niran DEM
  • 22. React Responsively, Render Responsibly Yoav Niran
  • 23. React Responsively, Render Responsibly Yoav Niran The Journey 1. Data go low 2. Time to leave the nest 3. Spread the love props 4. Just Memoize It TM 5. Open a Window
  • 24. React Responsively, Render Responsibly Yoav Niran Clue #3 - Pure & Shallow Equal const getPhotoItemProps = (photo, selected) => { return { item: takePhotoProps({...photo, selected}), }; }; const photoItemSelector = (state, props) => { const photo = state.photos .find((p) => p.public_id === props.id); const selected = !!~state.selected.indexOf(props.id); return getPhotoItemProps(photo, selected); }; export default connect( photoItemSelector, bindActions, )(RenderCounter(PhotoItem)); PhotoItem/PhotoItem.js
  • 25. React Responsively, Render Responsibly Yoav Niran Fix #3 - Spread the love props const getPhotoItemProps = (photo, selected) => { return { item: takePhotoProps({...photo, selected}), }; }; const photoItemSelector = (state, props) => { const photo = state.photos .find((p) => p.public_id === props.id); const selected = !!~state.selected.indexOf(props.id); return getPhotoItemProps(photo, selected); }; export default connect( photoItemSelector, bindActions, )(RenderCounter(PhotoItem)); PhotoItem/PhotoItem.js const getPhotoItemProps = (photo, selected) => { return { ...takePhotoProps({...photo, selected}), }; }; const photoItemSelector = (state, props) => { const photo = state.photos .find((p) => p.public_id === props.id); const selected = !!~state.selected.indexOf(props.id); return getPhotoItemProps(photo, selected); }; export default connect( photoItemSelector, bindActions, )(RenderCounter(PhotoItem));
  • 26. React Responsively, Render Responsibly Yoav Niran DEM
  • 27. React Responsively, Render Responsibly Yoav Niran
  • 28. React Responsively, Render Responsibly Yoav Niran Yay?
  • 29. React Responsively, Render Responsibly Yoav Niran
  • 30. React Responsively, Render Responsibly Yoav Niran Memoization “...memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again…” https://en.wikipedia.org/wiki/Memoization Memoization
  • 31. React Responsively, Render Responsibly Yoav Niran Clue #4 - Memoization
  • 32. React Responsively, Render Responsibly Yoav Niran The Journey 1. Data go low 2. Time to leave the nest 3. Spread the love props 4. Just Memoize It TM 5. Open a Window
  • 33. React Responsively, Render Responsibly Yoav Niran Fix #4 – Just memoize it PhotoItem/PhotoItem.selectors.js import {createSelector} from "reselect"; import {takePhotoProps} from "../../selectors"; const getPhoto = (state, props) => { return state.photos .find((p) => p.public_id === props.id); }; const selectIsSelectedById = (state, props) => !!~state.selected.indexOf(props.id); export const getPhotoByIdSelector = () => createSelector( [ getPhoto, selectIsSelectedById ], (photo, selected) => ({...takePhotoProps(photo), selected}), ); TM
  • 34. React Responsively, Render Responsibly Yoav Niran const getPhotoItemProps = (photo, selected) => { return { ...takePhotoProps({...photo, selected}), }; }; const photoItemSelector = (state, props) => { const photo = state.photos .find((p) => p.public_id === props.id); const selected = !!~state.selected.indexOf(props.id); return getPhotoItemProps(photo, selected); }; export default connect( photoItemSelector, bindActions, )(RenderCounter(PhotoItem)); PhotoItem/PhotoItem.js import {getPhotoByIdSelector} from "./PhotoItem.selectors const PhotoItem = (props) => { const {id, …, selected, price} = props.item, horizontal = props.horizontal; return ( <article> ... </article> ); }; export default connect( () => { const photoSelector = getPhotoByIdSelector(); return (state, props) => ({ ...photoSelector(state, props), }); }, bindActions, )(RenderCounter(PhotoItem)); Fix #4 – Just memoize it TM
  • 35. React Responsively, Render Responsibly Yoav Niran DEM
  • 36. React Responsively, Render Responsibly Yoav Niran
  • 37. React Responsively, Render Responsibly Yoav Niran Are we there yet? 39.35 ms
  • 38. React Responsively, Render Responsibly Yoav Niran DEM
  • 39. React Responsively, Render Responsibly Yoav Niran
  • 40. React Responsively, Render Responsibly Yoav Niran The Journey 1. Data go low 2. Time to leave the nest 3. Spread the love props 4. Just Memoize It TM 5. Open a Window
  • 41. React Responsively, Render Responsibly Yoav Niran Clue #5 – Letting it all out class PhotosGrid extends Component { render() { ... return ( <div className={cx(styles.container, "...")}> ... {photos.map((id) => ( <PhotoItem key={id} id={id}/> ))} </div> );}} ... PhotosGrid/PhotosGrid.js
  • 42. React Responsively, Render Responsibly Yoav Niran Fix #5 – Open a window https://bvaughn.github.io/forward-js-2017
  • 43. React Responsively, Render Responsibly Yoav Niran Fix #5 – Open a window https://react-window.now.sh https://github.com/bvaughn/react-window
  • 44. React Responsively, Render Responsibly Yoav Niran Fix #5 – Open a window class PhotosGrid extends Component { render() { ... return ( <div className={cx(styles.container, "...")}> ... {photos.map((id) => ( <PhotoItem key={id} id={id}/> ))} </div> );}} ... PhotosGrid/PhotosGrid.js import {FixedSizeGrid as Grid} from "react-window"; renderItems() { const {height, width, photos} = this.props; const colCount = Math.floor(width / 250), rowCount = photos.length / colCount; return <Grid ref={this.gridRef} columnCount={colCount} columnWidth={250} height={height} rowCount={(rowCount + 1)} rowHeight={270} width={width} onScroll={({scrollTop}) => { this.gridScrollTop = scrollTop; }}> {({columnIndex, rowIndex, style}) => <PhotoItem index={getItemIndex(columnIndex, rowIndex, colCount)} style={style}/>} </Grid> } …
  • 45. React Responsively, Render Responsibly Yoav Niran DEM
  • 46. React Responsively, Render Responsibly Yoav Niran
  • 47. React Responsively, Render Responsibly Yoav Niran DEM
  • 48. React Responsively, Render Responsibly Yoav Niran
  • 49. React Responsively, Render Responsibly Yoav Niran The road to performance 1. Data go low 2. Time to leave the nest 3. Spread the love props 4. Just Memoize It TM 5. Open a Window
  • 50. React Responsively, Render Responsibly Yoav Niran We made it ! ~600 ms ~10 ms
  • 51. React Responsively, Render Responsibly Yoav Niran Thank y u https://github.com/yoavniran/react-performance-demo @poeticGeek

Hinweis der Redaktion

  1. Who here thinks about performance throughout their dev process. And not just when things start to go wrong? Performance is a tricky business We certainly dont want to over-do it but usually we dont even think about it until its really bad and users start to feel the pain How many here had to re-work their app after serious performance issues were discovered? It isnt pleasant right?
  2. For the purpose of this talk I built this small app that lets me view photos from my Cloudinary account. At cloudinary we put a big emphasis on delivering high quality media with efficiency and performance. Its a big reason our customers rely on us to deliver media to their web and mobile apps. As a consequence we take performance very seriously in the apps we build ourselves In the next 14 minutes or so Id like to show how its possible to quickly get a sense of what is causing slowdowns in your react app and what can be done to fix it.
  3. The app is roughly made out of this component structure we’ll focus on the PhotosGrid and PhotoItems which are going to prove as performance bottlenecks
  4. Live demo - Show the app and the performance issues
  5. Obviously this is a problem. Selecting one item shouldnt cause this many components to re-render each time. We already see a considerable slowdown with only several hundreds items in the dom. Consider what will happen if we had thousands.
  6. Our click handler is now taking several hundreds of milliseconds to complete. And this is on a strong machine. We really want to be down to just a few tenth of milliseconds at the most or we start to interfere with the browser’s ability to respond quickly to events while also doing its job of running callbacks, doing repaints, reflow, etc. Ideally we would like to keep our interactions at below 16 ms to hit that 60 fps sweetspot. So this is where we are now and this is where we should end up with  
  7. the 5 areas well concentrate on In order to get to our goal
  8. Quick word about how the app’s state is structured. I have a photos array and in it each object represents a photo item.
  9. In my photos grid component im now getting the entire photos collection Then im iterating over it and passing the data down to the photo item
  10. But I don’t need all this data at the grid level. So lets refactor so we’re only getting the ids instead Now we just pass the id to the photo item component and let it do its own selection This way were moving the data use down the component tree
  11. And in my photo item I added a selector that uses the id it gets to retrieve the photo’s data
  12. Moving the data selection to the photo item instead of the photos grid
  13. The photos grid is still re-rendering and so are all the Photo items Simply moving the selection of the photo items from our PhotosGrid component wasnt enough.
  14. Notice that the selected flag is stored inside, nested in the photos array
  15. Im using an Immutable data structure every selection action is updating the nested object immutable data means its going to create a new object each time. The issue is of course that changing our state like this can cause every component that relies on this data structure to re-render if were not careful
  16. Separating out the selected state to its own array should ensure that the grid doesn’t simply re-render every time a selection is made
  17. Ok. we’re making progress - PhotosGrid isnt re-rendering. Thats good But why are all the photo items still re-rendering? This isnt supposed to happen right?
  18. We must remember that both React’s PureComponent and by default Connect from react-redux do a shallow compare on the props sent to our component. This means that its doing a strict equal on only the first level of the props object. Our selector currently creates a new prop called “Item” every time our photos array in the app’s state changes and the strict equality check will find its not equal to the previous update
  19. We fix this problem by spreading the Item’s properties into the props we send down to our component. Now whether its PureComponent or Connect that does the comparison they should find that for all other components nothing changed except for the one photo we selected.
  20. Demo spreading the props in the photo item
  21. Congratulations. Great job! We did it. Or did we? We got rid of all that unnecessary rendering but i have a sense something isnt completely right yet. Call it a hunch.
  22. Lets use the dev tools performance tab again to take a look at what is going on after we click an item
  23. There are several good memoization libraries out there that can help us do this right. However I prefer Reselect because it does this by default while supporting more advanced usage such as selector composition.
  24. We use Reselect’s createSelector method which accepts other selectors as the building blocks for our selector. In Reselect every selector is memoized so in case the input it receives is the same as before, that function will not be called again and its previous results will be used.
  25. Notice that I create a new selector for every PhotoItem component Since React is going to render multiple PhotoItem component instances. Each of those will need its own memoized selector for the caching to work
  26. Demo memoization in place
  27. I promised that we’ll get down to around 16ms and were still several times slower than that however We’ve done all we can for the photo item component there is another area we need to look at in order to improve our performance and thats the grid itself
  28. lets take a look at what happens when we go from our photo view back to the grid
  29. what we can see here is that we're naively rendering the entire photo collection whether we have just a few or thousands. All of them will be rendered to the dom at once!
  30. We need to take a windowing (virtualize) approach which means well only render the items that the user sees And a few before and after to support a smooth scrolling experience This prevents us from rendering the entire collection to the dom
  31. Instead of rendering the entire collection we use the fixed sized grid from react-window to render the items Theres a little more coding involved but its well worth it
  32. Lastly - Lets take a look at our photo select handler once the windowing technique in place.
  33. Lastly - Lets take a look at our photo select handler once the windowing technique in place.
  34. Lets review - We moved selecting data down the tree We’ve un-nested our data structure We’ve spread our props to help shallow compare We memoized our selector And we’ve added a window
  35. We did it ! We’re down from around 600 ms to only 10 !!!!!!!