React & Performance
Balram Singh
React Scotland
Bring Your Own Project
Venue: CR5Lloyds Banking Group, Sighthill, Edinburgh
Date: 12th April 2018
Time: 18:00 to 21:00
For latest updates please join on meetup
https://www.meetup.com/React-Scotland/
Agenda
- 18:00 - 18:15 Pizza and soft drinks
- 18:15 - 18:45 Integration Testing of React apps and APIs using Cypress.io - Sugandha Kumari
- 18:45 - 19:15 ES6 and ES7 features - Vijay Rana
- 19:15 - 19:45 Creating accessible React apps with React 16 - Balram Singh
- 19:45 - 20:00 Networking team
React
Re-render everything on every update
Sounds expensive?
Wasted renders
Problem
Re-render everything on every update.
But renders even waste components
Solution
Use shouldComponentUpdate
Initial state
Proposed state
Green = node that rerendered
Ideal update
Default behaviour
Orange = waster renderer
How to get this ideal update
Make shouldComponentUpdate checks fast
shouldComponentUpdate(nextProps) {
// expensive!
return isDeepEqual(this.props, nextProps);
}
const newValue = {
...oldValue
// any modifications you want to do
};
// fast check - only need to check references
newValue === oldValue; // false
// you can also use the Object.assign syntax if you prefer
const newValue2 = Object.assign({}, oldValue);
newValue2 === oldValue; // false
Bad practice
Good Practice
Make shouldComponentUpdate checks easy
Bad practice
shouldComponentUpdate (nextProps) {
// have any of the items changed?
if(!isArrayEqual(this.props.items, nextProps.items)){
return true;
}
// everything from here is horrible.
// if interaction has not changed at all then when can return false (yay!)
if(isObjectEqual(this.props.interaction, nextProps.interaction)){
return false;
}
// at this point we know:
// 1. the items have not changed
// 2. the interaction has changed
// we need to find out if the interaction change was relevant for us
const wasItemSelected = this.props.items.any(item => {
return item.id === this.props.interaction.selectedId
});
const isItemSelected = nextProps.items.any(item => {
return item.id === nextProps.interaction.selectedId
});
// return true when something has changed
// return false when nothing has changed
return wasItemSelected !== isItemSelected;
}
// Data structure with good separation of concerns (normalised data)
const state = {
items: [
{
id: 5,
description: 'some really cool item'
}
],
// an object to represent the users interaction with the system
interaction: {
selectedId: 5
}
};
Good Practice
Use Higher Order Components
- A higher-order component (HOC) is an advanced technique in React for reusing component logic.
- HOCs are not part of the React API, per se. They are a pattern that emerges from React's compositional nature.
- Concretely, a higher-order component is a function that takes a component and returns a new component
- Use HOCs like Recompose and connect
-
Use Normalised state
- Use lot of Connected components
export class TestContainer extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.callApi();
}
render() {
return (
<div>{this.props.data}</div>
);
}
}
const mapStateToProps = state => ({
data: state.data
});
const mapDispatchToProps = dispatch => ({
callApi: () => dispatch(callApi())
});
export default connect(mapStateToProps, mapDispatchToProps)(TestContainer);
Optimizing React Builds
-
Bundling
-
Minification
-
AOT (Ahead of Time compilation)
-
Tree Shaking (removing unused code)
-
Code Splitting (loading chunk of code on demand)
Where should I Code Split
-
When part of application is highly unlikely to be used
-
Component is heavy and can be used in multiple apps
Caution
Code splitting is cool, but don’t get carried away. If your entire application is a bunch of lazy components, you are a) defeating the purpose of the bundler, b)prematurely optimizing and c) making your code legitimately harder to read and debug.
class Heroes extends Component {
constructor(props) {
super(props);
this.state = {
...
lazyEditHero: null
}
}
async LoadEditForm() {
const { default : EditHero } = await import('./EditHero');
this.setState({ lazyEditHero: EditHero })
}
handleSelect = async hero => {
await this.LoadEditForm();
this.setState({ selectedHero: hero });
}
handleEnableAddMode = async () => {
await this.LoadEditForm();
this.setState({
addingHero: true,
selectedHero: { id: '', name: '', saying: '' }
});
}
...
render() {
const EditHero = this.state.lazyEditHero;
return (
<div>
...
<div className="editarea">
<button onClick={this.handleEnableAddMode}>Add</button>
{EditHero ? (
<EditHero
addingHero={this.state.addingHero}
onChange={this.handleOnChange}
selectedHero={this.state.selectedHero}
onSave={this.handleSave}
onCancel={this.handleCancel}
/>
) : (
<div></div>
)}
</div>
</div>
);
}
}
ReactJS Anti Patterns
-
Pure renderer Functions
-
Pure renderer Immutability
Pure renderer Functions
// BAD
export default (props, context) => {
// ... do expensive compute on props ...
return <SomeComponent {...props} />
}
// GOOD
import { pure } from 'recompose';
// See: https://github.com/acdlite/recompose#composition
// This won't be called when the props DONT change
export default pure((props, context) => {
// ... do expensive compute on props ...
return <SomeComponent someProp={props.someProp} />
})
Pure renderer Immutability
In order to preserve performance one needs to consider the creation of new entities in the render method.
Array methods such as slice, filter, map and reduce are also good example of immutability
Use spread operator (Do not use push() for array)
Spread operator for Objects
Spread operator for Objects
Best Practise
// NEVER do this
render() {
return <MyInput onChange={this.props.update.bind(this)} />;
}
// NEVER do this
render() {
return <MyInput onChange={() => this.props.update()} />;
}
// Instead do this
onChange() {
this.props.doUpdate()
}
render() {
return <MyInput onChange={this.onChange}/>;
}
// NEVER do this, if there are no items, SubComponent will render every time!
render() {
return <SubComponent items={this.props.items || []}/>
}
// This will avoid re-rendering
const EMPTY_ARRAY = []
render() {
return <SubComponent items={this.props.items || EMPTY_ARRAY}/>
}
Virtualize Long Lists
- React Virtualized
- Ag-grid
How to measure Performace
-
React Perf
-
React Opt
-
Profiling Components with the Chrome Performance Tab
References
- React Anti Pattern https://github.com/nfour/js-structures/blob/master/guides/react-anti-patterns.md
- Debugging React Performance with React 16 and Chrome dev tools https://building.calibreapp.com/debugging-react-performance-with-react-16-and-chrome-devtools-c90698a522ad
- ReactCasre#9 - Immutability in Java Script https://www.youtube.com/watch?v=4LzcQyZ9JOU
- React.js Conf 2015 - Making your app fast with high-performance components https://www.youtube.com/watch?v=KYzlpRvWZ6c
- Impress Your Friends With Code Splitting in React https://hackernoon.com/impress-your-friends-with-code-splitting-in-react-9f9a3ca2ae6e
- React performance tips https://medium.com/@joomiguelcunha/react-performance-tips-5fa199a450b2
- React/Redux Performance Tuning Tips https://medium.com/@arikmaor/react-redux-performance-tuning-tips-cef1a6c50759
- Performance optimizations for React applications: Round 2 https://medium.com/@alexandereardon/performance-optimisations-for-react-applications-round-2-2042e5c9af97
- Server Rendering, Code Splitting, and Lazy Loading with React Router v4 https://medium.com/airbnb-engineering/server-rendering-code-splitting-and-lazy-loading-with-react-router-v4-bfe596a6af70
- React performance optimisation tool https://github.com/reactopt/reactopt
- React.js pure render performance anti-pattern https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f
- Analyzing Critical Rendering Path Performance https://developers.google.com/web/fundamentals/performance/critical-rendering-path/analyzing-crp?hl=en
- Why did you update https://github.com/maicki/why-did-you-update
- High Performance React: 3 New Tools to Speed Up Your Apps https://medium.freecodecamp.org/make-react-fast-again-tools-and-techniques-for-speeding-up-your-react-app-7ad39d3c1b82
React & Performance
By Balram Singh
React & Performance
If you care about performance, it's fairly easy to make any React application super fast. Here is how.
- 1,957