React & Redux - Performance tips
Sebastián Bogado
Francisco Puyó
Render pipeline
Render pipeline
Render pipeline
Render pipeline
Render pipeline
Render pipeline
mapping state to props
import Todo from './Todo.jsx';
const mapStateToProps = (state, ownProps) => {
return {
todo: state.todos[ownProps.id],
}
}
export default connect(mapStateToProps, null)(Todo);
mapping state to props
const mapStateToProps = function mapStateToProps(state) {
return state;
}
Estamos retornando todo el objeto state
class App extends React.Component {
componentWillMount() {
this.props.getUser(this.props.locationBeforeTransitions.pathname);
}
render() {
return (
<DocumentTitle title={Translations.PAGE_TITLE_DEFAULT}>
<TransitionManager showSpinner={this.props.login.isFetchingUserFromToken}>
{ this.props.children }
</TransitionManager>
</DocumentTitle>
);
}
}
Y luego tenemos que buscar las propiedades que precisamos.
mapping state to props
- retornar sólo lo que el componente necesita
const mapStateToProps = function mapStateToProps(state) {
const pathname = state.route.locationBeforeTransitions.pathname;
const isFetchingUserFromToken = state.login.isFetchingUserFromToken;
return { pathname, isFetchingUserFromToken }
}
class App extends React.Component {
componentWillMount() {
this.props.getUser(this.props.pathname);
}
render() {
return (
<DocumentTitle title={Translations.PAGE_TITLE_DEFAULT}>
<TransitionManager showSpinner={this.props.isFetchingUserFromToken}>
{ this.props.children }
</TransitionManager>
</DocumentTitle>
);
}
}
mapping state to props
La interfaz del componente queda mucho más clara y definida
class App extends React.Component {
propTypes = {
children: React.PropTypes.object,
getUser: React.PropTypes.func,
isFetchingUserFromToken: React.PropTypes.bool,
pathName: React.PropTypes.string,
}
/*** Codigo del componente ***/
};
mapping state to props
const mapStateToProps = function mapStateToProps(state) {
const pathname = state.route.locationBeforeTransitions.pathname;
const isFetchingUserFromToken = state.login.isFetchingUserFromToken;
return { pathname, isFetchingUserFromToken };
}
const mapStateToProps = function mapStateToProps(state) {
return state
}
[
{
"_id": "5909e202bfc1e6d2ce07e931",
"index": 0,
"guid": "c7c6eb56-176b-4c95-8ada-f6dff11cea95",
"isActive": false,
"balance": "$1,426.78",
"picture": "http://placehold.it/32x32",
"age": 21,
"eyeColor": "green",
"name": "Rivers Ellison",
"gender": "male",
"company": "ISOLOGIX",
"email": "riversellison@isologix.com",
"phone": "+1 (907) 534-3406",
"address": "677 Grace Court, Canby, Connecticut, 600",
"about": "Sunt dolore occaecat veniam deserunt dolor officia. Ea aliqua sunt cupidatat ut laborum sunt nostrud adipisicing fugiat pariatur. Id mollit ipsum sint enim et. Laboris veniam dolore pariatur exercitation laborum anim dolore. Irure officia ex aliqua labore ea proident aute sunt nulla. Anim ex deserunt anim deserunt laborum est laborum labore cillum occaecat voluptate in aliquip.\r\n",
"registered": "2015-02-16T04:38:44 +03:00",
"latitude": -23.818232,
"longitude": 101.703476,
"tags": [
"eu",
"anim",
"deserunt",
"duis",
"amet",
"sit",
"pariatur"
],
"friends": [
{
"id": 0,
"name": "Parrish Villarreal"
},
{
"id": 1,
"name": "Betsy Mueller"
},
{
"id": 2,
"name": "Peck Salazar"
}
],
"greeting": "Hello, Rivers Ellison! You have 5 unread messages.",
"favoriteFruit": "strawberry"
},
{
"_id": "5909e2020ee0c00c5bd23942",
"index": 1,
"guid": "bcc55d66-66cf-4993-82b1-f7df8de8bc0d",
"isActive": true,
"balance": "$1,083.59",
"picture": "http://placehold.it/32x32",
"age": 32,
"eyeColor": "brown",
"name": "Hyde Snow",
"gender": "male",
"company": "STUCCO",
"email": "hydesnow@stucco.com",
"phone": "+1 (898) 517-2958",
"address": "982 Neptune Court, Kylertown, New York, 8396",
"about": "Et ipsum non cillum enim reprehenderit nisi minim pariatur esse. Minim ullamco tempor excepteur ad sint commodo esse et id non eu eu. Ex et mollit veniam ad. Irure do magna excepteur consectetur eu cillum adipisicing reprehenderit do. Ex ea voluptate enim est ad magna reprehenderit consequat mollit esse tempor.\r\n",
"registered": "2014-08-11T05:08:14 +03:00",
"latitude": -45.554253,
"longitude": 24.409109,
"tags": [
"laborum",
"fugiat",
"enim",
"aliqua",
"fugiat",
"irure",
"tempor"
],
"friends": [
{
"id": 0,
"name": "Jacobs Shannon"
},
{
"id": 1,
"name": "Jan Pena"
},
{
"id": 2,
"name": "Sallie Hamilton"
}
],
"greeting": "Hello, Hyde Snow! You have 10 unread messages.",
"favoriteFruit": "strawberry"
},
{
"_id": "5909e202abba6f2e7b1d6fdd",
"index": 2,
"guid": "54f40bef-54a1-4ab5-82e6-adce37226e68",
"isActive": false,
"balance": "$3,417.90",
"picture": "http://placehold.it/32x32",
"age": 28,
"eyeColor": "blue",
"name": "Merritt Bray",
"gender": "male",
"company": "UPLINX",
"email": "merrittbray@uplinx.com",
"phone": "+1 (840) 540-2695",
"address": "529 Tapscott Street, Movico, Idaho, 954",
"about": "In ad nisi reprehenderit esse commodo. Culpa ullamco ipsum enim voluptate labore consectetur amet esse et aliqua et. Nisi mollit sunt incididunt incididunt aliqua ipsum elit. Velit cupidatat velit est amet aliquip anim commodo amet minim dolore enim. Cupidatat sit adipisicing ullamco consequat tempor id in dolor eiusmod aliqua cillum est ullamco. Pariatur enim qui ad dolor officia laboris adipisicing in ea ut esse.\r\n",
"registered": "2014-07-03T11:19:57 +03:00",
"latitude": 24.491741,
"longitude": -145.470934,
"tags": [
"deserunt",
"sint",
"eiusmod",
"amet",
"irure",
"nulla",
"Lorem"
],
"friends": [
{
"id": 0,
"name": "Eleanor Delacruz"
},
{
"id": 1,
"name": "Turner Gross"
},
{
"id": 2,
"name": "Finch Mcneil"
}
],
"greeting": "Hello, Merritt Bray! You have 5 unread messages.",
"favoriteFruit": "banana"
},
{
"_id": "5909e20225e7ebac44e2d200",
"index": 3,
"guid": "1cdde354-6781-4ec0-bc49-33e9778e3313",
"isActive": false,
"balance": "$3,631.47",
"picture": "http://placehold.it/32x32",
"age": 38,
"eyeColor": "green",
"name": "Mcfarland Melton",
"gender": "male",
"company": "ASSISTIX",
"email": "mcfarlandmelton@assistix.com",
"phone": "+1 (841) 404-2770",
"address": "525 Royce Street, Lewis, Tennessee, 7008",
"about": "Laborum tempor officia sit consectetur voluptate deserunt et dolore sunt sit consectetur officia deserunt. Veniam amet sit eiusmod officia. Do sint ad fugiat sint deserunt reprehenderit aute irure. Dolore culpa non cupidatat in aute mollit. Et irure ut pariatur nisi sunt.\r\n",
"registered": "2014-01-06T06:24:48 +03:00",
"latitude": 85.011245,
"longitude": 168.285699,
"tags": [
"sint",
"aliqua",
"sint",
"officia",
"laborum",
"occaecat",
"officia"
],
"friends": [
{
"id": 0,
"name": "Grimes Holder"
},
{
"id": 1,
"name": "Marlene Hardy"
},
{
"id": 2,
"name": "Juliette Hines"
}
],
"greeting": "Hello, Mcfarland Melton! You have 10 unread messages.",
"favoriteFruit": "strawberry"
},
{
"_id": "5909e20212221cccf0809b8e",
"index": 4,
"guid": "fa43fc9e-cd11-42f9-98f3-cd6252b2cb2e",
"isActive": false,
"balance": "$2,045.81",
"picture": "http://placehold.it/32x32",
"age": 34,
"eyeColor": "blue",
"name": "Raquel Drake",
"gender": "female",
"company": "FURNAFIX",
"email": "raqueldrake@furnafix.com",
"phone": "+1 (954) 543-3334",
"address": "588 Kenilworth Place, Smock, Hawaii, 6260",
"about": "Sit irure minim consectetur eu. Non quis Lorem duis pariatur. Reprehenderit magna in voluptate anim.\r\n",
"registered": "2017-04-04T07:44:44 +03:00",
"latitude": 87.95867,
"longitude": -64.982767,
"tags": [
"proident",
"ad",
"sit",
"culpa",
"id",
"dolore",
"nisi"
],
"friends": [
{
"id": 0,
"name": "Constance Mcgowan"
},
{
"id": 1,
"name": "Marsha Becker"
},
{
"id": 2,
"name": "Tracey Kinney"
}
],
"greeting": "Hello, Raquel Drake! You have 4 unread messages.",
"favoriteFruit": "strawberry"
},
{
"_id": "5909e202f9e951a59069b6f5",
"index": 5,
"guid": "290617f8-c0f4-47ac-a162-c2f0d25f6c3f",
"isActive": true,
"balance": "$1,845.49",
"picture": "http://placehold.it/32x32",
"age": 27,
"eyeColor": "green",
"name": "Petty Mccall",
"gender": "male",
"company": "ZILLACOM",
"email": "pettymccall@zillacom.com",
"phone": "+1 (990) 553-2406",
"address": "210 Colin Place, Biddle, Alabama, 697",
"about": "Consectetur amet irure sit nisi quis magna eu nostrud aliquip. Do minim duis tempor id irure amet occaecat. Commodo elit exercitation ea ad anim deserunt voluptate sit qui pariatur exercitation velit occaecat. Pariatur cillum id occaecat esse. Ipsum sit non culpa dolor.\r\n",
"registered": "2015-02-01T04:51:30 +03:00",
"latitude": 39.788631,
"longitude": -119.798066,
"tags": [
"incididunt",
"pariatur",
"commodo",
"proident",
"mollit",
"culpa",
"fugiat"
],
"friends": [
{
"id": 0,
"name": "Mitzi Reyes"
},
{
"id": 1,
"name": "Ava Best"
},
{
"id": 2,
"name": "Celeste Weber"
}
],
"greeting": "Hello, Petty Mccall! You have 5 unread messages.",
"favoriteFruit": "apple"
},
{
"_id": "5909e20245a110dbfba5553c",
"index": 6,
"guid": "2ece983f-af14-4cf5-a8d0-b1cebe470f19",
"isActive": false,
"balance": "$2,377.02",
"picture": "http://placehold.it/32x32",
"age": 25,
"eyeColor": "green",
"name": "Laurie Valdez",
"gender": "female",
"company": "AQUAFIRE",
"email": "laurievaldez@aquafire.com",
"phone": "+1 (949) 498-2402",
"address": "569 Hampton Avenue, Fairfield, Maryland, 684",
"about": "Minim magna dolore officia velit exercitation magna aliquip labore. Cupidatat Lorem aliqua cillum quis voluptate exercitation sint. In dolor velit sunt sit commodo officia anim nisi qui excepteur pariatur.\r\n",
"registered": "2015-04-06T11:12:55 +03:00",
"latitude": 5.557238,
"longitude": 102.973884,
"tags": [
"est",
"cillum",
"ea",
"pariatur",
"amet",
"sunt",
"nisi"
],
"friends": [
{
"id": 0,
"name": "Vega Talley"
},
{
"id": 1,
"name": "Susie Holden"
},
{
"id": 2,
"name": "Krista Nolan"
}
],
"greeting": "Hello, Laurie Valdez! You have 2 unread messages.",
"favoriteFruit": "banana"
}
]
{
"pathname": "/sandbox",
"isFetchingUserFromToken": true,
}
mapping state to props
const getPersonStats = (person) => {
const stats = expensiveFunction(person);
const children = person.children ?
person.children.map(getPersonStats) : [];
return {
stats,
children,
};
}
const getFamilyStats = (state) => state.familyPersons.map(getPersonStats);
const mapStateToProps = (state) => ({
getFamilyTree: getFamilyStats(state),
});
connect(mapStateToProps)(FamilyTreeRecursiveComponent)
Reselect
import { createSelector } from 'reselect';
const getPersonStats = (person) => {
const stats = expensiveFunction(person);
const children = person.children ?
person.children.map(getPersonStats) : [];
return {
stats,
children,
};
}
const getFamilyPersons = (state, familyId) => state.familyPersons[familyId];
const getFamilyStats = createSelector(
getFamilyPersons,
(persons) => persons.map(getPersonStats)
);
const mapStateToProps = (state, props) => ({
getFamilyTree: getFamilyStats(state, props.familyId),
});
connect(mapStateToProps)(FamilyTreeRecursiveComponent)
import { getFamilyStats } from './FamilyTree.selector.js'
const mapStateToProps = (state, props) => ({
getFamilyTree: getFamilyStats(state, props.familyId),
});
connect(mapStateToProps)(FamilyTreeRecursiveComponent)
RESELECT WITH MULTIPLE INSTANCES
const makeGetFamilyStats = () => createSelector(
getFamilyPersons,
(persons) => persons.map(getPersonStats)
);
const makeMapStateToProps = () => {
const getFamilyStats = makeGetFamilyStats();
const mapStateToProps = (state, props) => ({
getFamilyTree: getFamilyStats(state, props.familyId),
});
return mapStateToProps;
};
connect(makeMapStateToProps)(FamilyTreeRecursiveComponent)
Mapping state to props
- Pasar sólo las propiedades necesarias al componente
- Usar funciones puras
- Usar Reselect
ShouldComponentUpdate
SCU - pure components
- implementa un SCU que realiza un chequeo shallow en las props y el state
- en vez de extender de
React.Component
, extendemos deReact.PureComponent
SCU - pure components
class ChildComponent extends React.Component {
render() {
return <span>{this.props.text}</span>;
}
}
class ChildPureComponent extends React.PureComponent {
render() {
return <span>{this.props.text}</span>;
}
}
class ParentComponent extends React.Component {
render() {
this.state.text = 'hello';
setInterval(() => this.setState({text: 'hello'}), 1000)
}
render() {
return (
<ChildComponent text={this.state.text} />
<ChildPureComponent text={this.state.text} />
);
}
}
SCU - custom
class GridToolbar extends React.Component {
shouldComponentUpdate() {
return false;
}
render() {
return (
<OptionsPanel>
{ this.props.toolbar }
{ this.props.displayOptions }
</OptionsPanel>
);
}
}
GridToolbar.propTypes = {
toolbar: React.PropTypes.node,
displayOptions: React.PropTypes.node.isRequired,
};
GridToolbar.defaultProps = {
toolbar: <Toolbar />,
displayOptions: <DisplayOptions />,
};
Render function
render function - cortocircuito
render() {
if (this.state.someCondition) return null;
return (
<div>
<MyComplexChild />
<MySecondComplexChild />
</div>
);
}
render function - No crear objetos ni funciones
render() {
return (
<div>
<MyChild
items={this.props.items || []}
handleChange={(val) => this.someValue = val;}
handleSomething={handleSomething.bind(this)}
/>
</div>
);
}
render function - No crear objetos ni funciones
class MyComponent extends React.Component {
handleChange = (val) => this.someValue = val
handleSomething = ({ text }) => this.props.someCallback(text)
render() {
return (
<div>
<MyChild
items={this.props.items}
handleChange={handleChange}
handleSomething={handleSomething}
/>
</div>
);
}
}
MyComponent.defaultProps = {
items: [],
};
render function - usar keys cuando hay listas
<ul>
<li>yasten</li>
<li>danee</li>
</ul>
<ul>
<li>yasten</li>
<li>danee</li>
<li>matse</li>
</ul>
<ul>
<li>yasten</li>
<li>danee</li>
</ul>
<ul>
<li>matse</li>
<li>yasten</li>
<li>danee</li>
</ul>
render function - usar keys cuando hay listas
<ul>
<li key="puyo">yasten</li>
<li key="mata">danee</li>
</ul>
<ul>
<li key="cesp">matse</li>
<li key="puyo">yasten</li>
<li key="mata">danee</li>
</ul>
React performance tools
npm install --save-dev react-perf-addons
React performance tools
Perf.printInclusive();
Perf.printExclusive();
React performance tools
Perf.printWasted();
React Devtools - trace updates
React Devtools - trace updates
React Devtools - trace updates
highlights
-
mapStateToProps
pura, precisa e inteligente. Si es necesario, HOF
-
PureComponent
s
- Medir -> probar mejora -> medir
-
render
's que no creen nuevos objetos
Preguntas?
Gracias!
React + Redux - Performance
By Francisco Puyó
React + Redux - Performance
- 1,061