2018-03-28 18:19:44 +03:00
|
|
|
# Writing efficient React code
|
|
|
|
|
|
|
|
In this article we'll discuss about the various component types we can use, as
|
|
|
|
well as discuss some tips to make your React application faster.
|
|
|
|
|
|
|
|
## TL;DR tips
|
|
|
|
|
|
|
|
* Prefer props and state immutability and use `PureComponent` components as a default
|
|
|
|
* As a convention, the object reference should change **if and only if** the inner data
|
|
|
|
changes.
|
|
|
|
* Be careful to never use new instance of functions as props to a Component (it's fine to use
|
|
|
|
them as props to a DOM element).
|
|
|
|
* Be careful to not update a reference if the inner data doesn't change.
|
|
|
|
* [Always measure before optimizing](./performance.md) to have a real impact on
|
|
|
|
performance. And always measure _after_ optimizing too, to prove your change
|
|
|
|
had a real impact.
|
|
|
|
|
|
|
|
## How React renders normal components
|
|
|
|
|
|
|
|
### What's a normal component?
|
|
|
|
As a start let's discuss about how React renders normal plain components, that
|
|
|
|
don't use `shouldComponentUpdate`. What we call plain components here are either:
|
|
|
|
* classes that extend [`Component`](https://reactjs.org/docs/react-component.html)
|
|
|
|
```jsx
|
|
|
|
class Application extends React.Component {
|
|
|
|
render() {
|
|
|
|
return <div>{this.props.content}</div>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* normal functions that take some `props` as parameter and return some JSX. We
|
|
|
|
call these functions either Stateless Components or Functional Components.
|
|
|
|
This is important to understand that these Stateless Components are _not_
|
|
|
|
especially optimized in React.
|
|
|
|
```jsx
|
|
|
|
function Application(props) {
|
|
|
|
return <div>{props.content}</div>;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
These functions are equivalent to classes extending `Component`. In
|
|
|
|
the rest of the article we'll especially focus on the latter. Unless otherwise
|
|
|
|
stated everything about classes extending `Component` is also true for
|
|
|
|
Stateless/Functional Components.
|
|
|
|
|
|
|
|
#### Notes on the use of JSX
|
|
|
|
Because we don't use a build step in mozilla-central yet, some of our
|
|
|
|
tools don't use JSX and use [factories](https://reactjs.org/docs/react-api.html#createfactory)
|
|
|
|
instead:
|
|
|
|
```javascript
|
|
|
|
class Application extends React.Component {
|
|
|
|
render() {
|
|
|
|
return dom.div(null, this.props.content);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
We'll use JSX in this documentation for more clarity but this is strictly
|
|
|
|
equivalent. You can read more on [React documentation](https://reactjs.org/docs/react-without-jsx.html).
|
|
|
|
|
|
|
|
### The first render
|
|
|
|
There's only one way to start a React application and trigger a first render:
|
|
|
|
calling `ReactDOM.render`:
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
ReactDOM.render(
|
|
|
|
<Application content='Hello World!'/>,
|
|
|
|
document.getElementById('root')
|
|
|
|
);
|
|
|
|
```
|
|
|
|
|
|
|
|
React will call that component's `render` method, and then recursively call
|
|
|
|
every child's `render` method, generating a rendering tree and then a virtual
|
|
|
|
DOM tree. It will then render actual DOM elements to the specified container.
|
|
|
|
|
|
|
|
### Subsequent rerenders
|
|
|
|
|
|
|
|
There are several ways to trigger a rerender:
|
|
|
|
1. We call `ReactDOM.render` again with the same component.
|
|
|
|
```jsx
|
|
|
|
ReactDOM.render(
|
|
|
|
<Application content='Good Bye, Cruel World!'/>,
|
|
|
|
document.getElementById('root')
|
|
|
|
);
|
|
|
|
```
|
|
|
|
2. One component's state changes, through the use of [`setState`](https://reactjs.org/docs/react-component.html#setstate).
|
|
|
|
If the application is using Redux, this is how Redux-connected components
|
|
|
|
trigger updates too.
|
|
|
|
3. One component's props change. But note that this can't happen by itself, this
|
|
|
|
is always a consequence of the case 1 or 2 in one of its parents. So we'll
|
|
|
|
ignore this case for this chapter.
|
|
|
|
|
|
|
|
When one of these happens, just like the initial render, React will call that
|
|
|
|
component's `render` method, and then recursively call every child's `render`
|
|
|
|
method, but this time possibly with changed props compared to the previous render.
|
|
|
|
|
|
|
|
These recursive calls produce a new rendering tree. That's where React uses an
|
|
|
|
algorithm called _virtual diffing_ or
|
|
|
|
[_reconciliation_](https://reactjs.org/docs/reconciliation.html) to find the
|
|
|
|
minimal set of updates to apply to the DOM. This is good because the less
|
|
|
|
updates to the DOM the less work the browser has to do to reflow and repaint the
|
|
|
|
application.
|
|
|
|
|
|
|
|
### Main sources of performance issues
|
|
|
|
|
|
|
|
From this explanation we can gather that the main performance issues can
|
|
|
|
come from:
|
|
|
|
1. triggering the render process **too frequently**,
|
|
|
|
2. **expensive** render methods,
|
|
|
|
3. the reconciliation algorithm itself. The algorithm is O(n) according to React
|
|
|
|
authors, which means the processing duration increases linearly with **the number
|
|
|
|
of elements in the tree** we compare. So a larger tree means a longer time to
|
|
|
|
process.
|
|
|
|
|
|
|
|
Let's dive more into each one of these issues.
|
|
|
|
|
|
|
|
#### Do not render too often
|
|
|
|
|
|
|
|
A rerender will happen after calling `setState` to change the
|
|
|
|
local state.
|
|
|
|
|
|
|
|
Everything that's in the state should be used in `render`.
|
|
|
|
Anything in the state that's not used in `render` shouldn't be in the state, but
|
|
|
|
rather in an instance variable. This way you won't trigger an update if you
|
|
|
|
change some internal state that you don't want to reflect in the UI.
|
|
|
|
|
|
|
|
If you call `setState` from an event handler you may call it too often.
|
|
|
|
This is usually not a problem because React is smart enough to merge close
|
|
|
|
setState calls and trigger a rerender only once per frame. Yet if your `render`
|
|
|
|
is expensive (see below as well) this could lead to problems and you may want to
|
|
|
|
use `setTimeout` or other similar techniques to throttle the renders.
|
|
|
|
|
|
|
|
#### Keep `render` methods as lean as possible
|
|
|
|
|
|
|
|
When rendering a list, it's very common that we'll map this list to a list of
|
|
|
|
components. This can be costly and we might want to cut this list in several
|
|
|
|
chunks of items or to
|
|
|
|
[virtualize this list](https://reactjs.org/docs/optimizing-performance.html#virtualize-long-lists).
|
|
|
|
Although this is not always possible or easy.
|
|
|
|
|
|
|
|
Do not do heavy computations in your `render` methods. Rather do them before
|
|
|
|
setting the state, and set the state to the result of these computations.
|
|
|
|
Ideally `render` should be a direct mirror of the component's props and state.
|
|
|
|
|
|
|
|
Note that this rule also applies to the other methods called as part of the
|
|
|
|
rendering process: `componentWillUpdate` and `componentDidUpdate`. In
|
|
|
|
`componentDidUpdate` especially avoid synchronous reflows by getting DOM
|
|
|
|
measurements, and do not call `setState` as this would trigger yet another
|
|
|
|
update.
|
|
|
|
|
|
|
|
#### Help the reconciliation algorithm be efficient
|
|
|
|
|
|
|
|
The smaller the tree is, the faster the algorithm is. So it's
|
|
|
|
useful to limit the changes to a subtree of the full tree. Note that the use of
|
|
|
|
`shouldComponentUpdate` or `PureComponent` alleviates this issue by cutting off
|
|
|
|
entire branches from the rendering tree, [we discuss this in more details
|
2021-06-07 22:28:40 +03:00
|
|
|
below](#shouldcomponentupdate-and-purecomponent-avoiding-renders-altogether).
|
2018-03-28 18:19:44 +03:00
|
|
|
|
|
|
|
Try to change the state as close as possible to where your UI
|
|
|
|
should change (close in the components tree).
|
|
|
|
|
|
|
|
Do not forget to [set `key` attributes when rendering a list of
|
|
|
|
things](https://reactjs.org/docs/lists-and-keys.html), which shouldn't be the
|
|
|
|
array's indices but something that identifies the item in a predictable, unique
|
|
|
|
and stable way. This helps the algorithm
|
|
|
|
a lot by skipping parts that likely haven't changed.
|
|
|
|
|
|
|
|
### More documentation
|
|
|
|
|
|
|
|
The React documentation has [a very well documented page](https://reactjs.org/docs/implementation-notes.html#mounting-as-a-recursive-process)
|
|
|
|
explaining the whole render and rerender process.
|
|
|
|
|
|
|
|
## `shouldComponentUpdate` and `PureComponent`: avoiding renders altogether
|
|
|
|
|
|
|
|
React has an optimized algorithm to apply changes. But the fastest algorithm is
|
|
|
|
an algorithm that isn't executed at all.
|
|
|
|
|
|
|
|
[React's own documentation about performance](https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate-in-action)
|
|
|
|
is quite complete on this subject.
|
|
|
|
|
|
|
|
### Avoiding rerenders with `shouldComponentUpdate`
|
|
|
|
|
|
|
|
As the first step of a rerender process, React calls your component's
|
|
|
|
[`shouldComponentUpdate`](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)
|
|
|
|
method with 2 parameters: the new props, and the new
|
|
|
|
state. If this method returns false, then React will skip the render process for this
|
|
|
|
component, **and its whole subtree**.
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
class ComplexPanel extends React.Component {
|
|
|
|
// Note: this syntax, new but supported by Babel, automatically binds the
|
|
|
|
// method with the object instance.
|
|
|
|
onClick = () => {
|
|
|
|
this.setState({ detailsOpen: true });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return false to avoid a render
|
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
|
|
|
// Note: this works only if `summary` and `content` are primitive data
|
|
|
|
// (eg: string, number) or immutable data
|
|
|
|
// (keep reading to know more about this)
|
|
|
|
return nextProps.summary !== this.props.summary
|
|
|
|
|| nextProps.content !== this.props.content
|
|
|
|
|| nextState.detailsOpen !== this.state.detailsOpen;
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<ComplexSummary summary={this.props.summary} onClick={this.onClick}/>
|
|
|
|
{this.state.detailsOpen
|
|
|
|
? <ComplexContent content={this.props.content} />
|
|
|
|
: null}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
__This is a very efficient way to improve your application speed__, because this
|
|
|
|
avoids everything: both calling render methods for this component _and_ the
|
|
|
|
whole subtree, and the reconciliation phase for this subtree.
|
|
|
|
|
|
|
|
Note that just like the `render` method, `shouldComponentUpdate` is called once
|
|
|
|
per render cycle, so it needs to be very lean and return as fast as possible. So
|
|
|
|
it should execute some cheap comparisons only.
|
|
|
|
|
|
|
|
### `PureComponent` and immutability
|
|
|
|
|
|
|
|
A very common implementation of `shouldComponentUpdate` is provided by React's
|
|
|
|
[`PureComponent`](https://reactjs.org/docs/react-api.html#reactpurecomponent):
|
|
|
|
it will shallowly check the new props and states for reference equality.
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
class ComplexPanel extends React.PureComponent {
|
|
|
|
// Note: this syntax, new but supported by Babel, automatically binds the
|
|
|
|
// method with the object instance.
|
|
|
|
onClick = () => {
|
|
|
|
// Running this repeatidly won't render more than once.
|
|
|
|
this.setState({ detailsOpen: true });
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<ComplexSummary summary={this.props.summary} onClick={this.onClick}/>
|
|
|
|
{this.state.detailsOpen
|
|
|
|
? <ComplexContent content={this.props.content} />
|
|
|
|
: null}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
This has a very important consequence: for non-primitive props and states, that is
|
|
|
|
objects and arrays that can be mutated without changing the reference itself,
|
|
|
|
PureComponent's inherited `shouldComponentUpdate` will yield wrong results and will
|
|
|
|
skip renders where it shouldn't.
|
|
|
|
|
|
|
|
So you're left with one of these two options:
|
|
|
|
* either implement your own `shouldComponentUpdate` in a `Component`
|
|
|
|
* or (__preferred__) decide to make all your data structure immutable.
|
|
|
|
|
|
|
|
The latter is recommended because:
|
|
|
|
* It's much simpler to think about.
|
|
|
|
* It's much faster to check for equality in `shouldComponentUpdate` and in other
|
|
|
|
places (like Redux' selectors).
|
|
|
|
|
|
|
|
Note you could technically implement your own `shouldComponentUpdate` in a
|
|
|
|
`PureComponent` but this is quite useless because `PureComponent` is nothing
|
|
|
|
more than `Component` with a default implementation for `shouldComponentUpdate`.
|
|
|
|
|
|
|
|
### About immutability
|
|
|
|
#### What it doesn't mean
|
|
|
|
It doesn't mean you need to enforce the immutability using a library like
|
|
|
|
[Immutable](https://github.com/facebook/immutable-js).
|
|
|
|
|
|
|
|
#### What it means
|
|
|
|
It means that once a structure exists, you don't mutate it.
|
|
|
|
|
2019-01-30 17:16:08 +03:00
|
|
|
**Every time some data changes, the object reference must change as well**. This
|
2018-03-28 18:19:44 +03:00
|
|
|
means a new object or a new array needs to be created. This gives the nice
|
|
|
|
reverse guarantee: if the object reference has changed, the data has changed.
|
|
|
|
|
|
|
|
It's good to go one step further to get a **strict equivalence**: if the data
|
|
|
|
doesn't change, the object reference mustn't change. This isn't necessary for
|
|
|
|
your app to work, but this is a lot better for performance as this avoids
|
|
|
|
spurious rerenders.
|
|
|
|
|
|
|
|
Keep reading to learn how to proceed.
|
|
|
|
|
|
|
|
#### Keep your state objects simple
|
|
|
|
|
|
|
|
Updating your immutable state objects can be difficult if the objects used are
|
|
|
|
complex. That's why it's a good idea to keep the objects simple, especially keep
|
|
|
|
them not nested, so that you don't need to use a library like
|
|
|
|
[immutability-helper](https://github.com/kolodny/immutability-helper),
|
|
|
|
[updeep](https://github.com/substantial/updeep), or even
|
|
|
|
[Immutable](https://github.com/facebook/immutable-js). Be especially careful
|
|
|
|
with Immutable as it's easy to create performance problems by misusing
|
|
|
|
its API.
|
|
|
|
|
|
|
|
If you're using Redux ([see below as well](#a-few-words-about-redux)) this
|
|
|
|
advice applies to your individual reducers as well, even if Redux tools make
|
|
|
|
it easy to have a nested/combined state.
|
|
|
|
|
|
|
|
#### How to update an object
|
|
|
|
|
|
|
|
Updating an object is quite easy.
|
|
|
|
|
|
|
|
You must not change/add/delete inner properties directly:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// Note that in the following examples we use the callback version
|
|
|
|
// of `setState` everywhere, because we build the new state from
|
|
|
|
// the current state.
|
|
|
|
|
|
|
|
// Please don't do this as this will likely induce bugs.
|
|
|
|
this.setState(state => {
|
|
|
|
state.stateObject.details = details;
|
|
|
|
return state;
|
|
|
|
});
|
|
|
|
|
|
|
|
// This is wrong too: `stateObject` is still mutated.
|
|
|
|
this.setState(({ stateObject }) => {
|
|
|
|
stateObject.details = details;
|
|
|
|
return { stateObject };
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
Instead **you must create a new object** for this property. In this example
|
|
|
|
we'll use the object spread operator, already implemented in Firefox, Chrome and Babel.
|
|
|
|
|
|
|
|
However here we take care to return the same object if it doesn't need an update. The
|
|
|
|
comparison happens inside the callback because it depends on the state as
|
|
|
|
well. This is a good thing to do so that the shallow equality check doesn't
|
|
|
|
return false if nothing changes.
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// Updating one property in the state
|
|
|
|
this.setState(({ stateObject }) => ({
|
|
|
|
stateObject: stateObject.content === newContent
|
|
|
|
? stateObject
|
|
|
|
: { ...stateObject, content: newContent },
|
|
|
|
});
|
|
|
|
|
|
|
|
// This is very similar if 2 properties need an update:
|
|
|
|
this.setState(({ stateObject1, stateObject2 }) => ({
|
|
|
|
stateObject1: stateObject1.content === newContent
|
|
|
|
? stateObject1
|
|
|
|
: { ...stateObject1, content: newContent },
|
|
|
|
stateObject2: stateObject2.details === newDetails
|
|
|
|
? stateObject2
|
|
|
|
: { ...stateObject2, details: newDetails },
|
|
|
|
});
|
|
|
|
|
|
|
|
// Or if one of the properties needs to update 2 of it's own properties:
|
|
|
|
this.setState(({ stateObject }) => ({
|
|
|
|
stateObject: stateObject.content === newContent && stateObject.details === newDetails
|
|
|
|
? stateObject
|
|
|
|
: { ...stateObject, content: newContent, details: newDetails },
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that this isn't about the returned `state` object, but its properties.
|
|
|
|
The returned object is always merged into the current state, and React creates
|
|
|
|
a new component's state object at each update cycle.
|
|
|
|
|
|
|
|
#### How to update an array
|
|
|
|
Updating an array is easy too.
|
|
|
|
|
|
|
|
You must avoid methods that mutate the array like push/splice/pop/shift and you
|
|
|
|
must not change directly an item.
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// Please don't do this as this will likely induce bugs.
|
|
|
|
this.setState(({ stateArray }) => {
|
|
|
|
stateArray.push(newItem); // This is wrong
|
|
|
|
stateArray[1] = newItem; // This is wrong too
|
|
|
|
return { stateArray };
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
Instead here again you need to **create a new array instance**.
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// Adding an element is easy.
|
|
|
|
this.setState(({ stateArray }) => ({
|
|
|
|
stateArray: [...stateArray, newElement],
|
|
|
|
}));
|
|
|
|
|
|
|
|
this.setState(({ stateArray }) => {
|
|
|
|
// Removing an element is more involved.
|
|
|
|
const newArray = stateArray.filter(element => element !== removeElement);
|
|
|
|
// or
|
|
|
|
const newArray = [...stateArray.slice(0, index), ...stateArray.slice(index + 1)];
|
|
|
|
// or do what you want on a new clone:
|
|
|
|
const newArray = stateArray.slice();
|
|
|
|
return {
|
|
|
|
// Because we want to keep the old array if removeElement isn't in the
|
|
|
|
// filtered array, we compare the lengths.
|
|
|
|
// We still start a render phase because we call `setState`, but thanks to
|
|
|
|
// PureComponent's shouldComponentUpdate implementation we won't actually render.
|
|
|
|
stateArray: newArray.length === stateArray.length ? stateArray : newArray,
|
|
|
|
};
|
|
|
|
|
|
|
|
// You can also return a falsy value to avoid the render cycle at all:
|
|
|
|
return newArray.length === stateArray.length
|
|
|
|
? null
|
|
|
|
: { stateArray: newArray };
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
#### How to update Maps and Sets
|
|
|
|
The process is very similar for Maps and Sets. Here is a quick example:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// For a Set
|
|
|
|
this.setState(({ stateSet }) => {
|
|
|
|
if (!stateSet.has(value)) {
|
|
|
|
stateSet = new Set(stateSet);
|
|
|
|
stateSet.add(value);
|
|
|
|
}
|
|
|
|
return { stateSet };
|
|
|
|
});
|
|
|
|
|
|
|
|
// For a Map
|
|
|
|
this.setState(({ stateMap }) => {
|
|
|
|
if (stateMap.get(key) !== value) {
|
|
|
|
stateMap = new Map(stateMap);
|
|
|
|
stateMap.set(key, value);
|
|
|
|
}
|
|
|
|
return { stateMap };
|
|
|
|
}));
|
|
|
|
```
|
|
|
|
|
|
|
|
#### How to update primitive values
|
|
|
|
|
|
|
|
Obviously, with primitive types like boolean, number or string, that are
|
|
|
|
comparable with the operator `===`, it's much easier:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
this.setState({
|
|
|
|
stateString: "new string",
|
|
|
|
stateNumber: 42,
|
|
|
|
stateBool: false,
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that we don't use the callback version of `setState` here. That's because
|
|
|
|
for primitive values we don't need to use the previous state to generate a new
|
|
|
|
state.
|
|
|
|
|
|
|
|
#### A few words about Redux
|
|
|
|
|
|
|
|
When working with Redux, the rules stay the same, except all of this
|
|
|
|
happens in your reducers instead of in your components. With Redux comes the
|
|
|
|
function [`combineReducers`](https://redux.js.org/docs/api/combineReducers.html)
|
|
|
|
that obeys all the rules we outlined before while making it possible to have a
|
|
|
|
nested state.
|
|
|
|
|
|
|
|
### `shouldComponentUpdate` or `PureComponent`?
|
|
|
|
|
|
|
|
It is highly recommended to go the full **PureComponent + immutability** route,
|
|
|
|
instead of writing custom `shouldComponentUpdate` implementations for
|
|
|
|
components. This is more generic, more maintainable, less error-prone, faster.
|
|
|
|
|
|
|
|
Of course all rules have exceptions and you're free to implement a
|
|
|
|
`shouldComponentUpdate` method if you have specific cases to take care of.
|
|
|
|
|
|
|
|
### Some gotchas with `PureComponent`
|
|
|
|
|
|
|
|
Because `PureComponent` shallowly checks props and state, you need to take care
|
|
|
|
to not create a new reference for something that's otherwise identical. Some
|
|
|
|
common cases are:
|
|
|
|
|
|
|
|
* Using a new instance for a prop at each render cycle. Especially, do not use
|
|
|
|
a bound function or an anonymous function (both classic functions or
|
|
|
|
arrow functions) as a prop:
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
render() {
|
|
|
|
return <MyComponent onUpdate={() => this.update()} />;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Each time the `render` method runs, a new function will be created, and in
|
|
|
|
`MyComponent`'s `shouldComponentUpdate` the shallow check will always fail
|
|
|
|
defeating its purpose.
|
|
|
|
|
|
|
|
* Using another reference for the same data. One very common example is the empty
|
|
|
|
array: if you use a new `[]` for each render, you won't skip render. A solution
|
|
|
|
is to reuse a common instance. Be careful as this can very well be hidden
|
|
|
|
within some complicated Redux reducers.
|
|
|
|
|
|
|
|
* A similar issue can arise if you use sets or maps. If you add an element in a
|
|
|
|
`Set` that's already in there, you don't need to return a new `Set` as it will be
|
|
|
|
identical.
|
|
|
|
|
|
|
|
* Be careful with array's methods, especially `map` or `filter`, as they always
|
|
|
|
return a new array. So even with the same inputs (same input array, same
|
|
|
|
function), you'll get a new output, even if it contains the same data. If
|
|
|
|
you're using Redux, [reselect](https://github.com/reactjs/reselect) is
|
|
|
|
recommended.
|
|
|
|
[memoize-immutable](https://github.com/memoize-immutable/memoize-immutable)
|
|
|
|
can be useful in some cases too.
|
|
|
|
|
|
|
|
## Diagnosing performance issues with some tooling
|
|
|
|
|
|
|
|
[You can read about it in the dedicated
|
|
|
|
page](./performance.md#diagnosing-performance-issues-in-react-based-applications).
|
|
|
|
|
|
|
|
## Breaking the rules: always measure first
|
|
|
|
|
|
|
|
You should generally follow these rules because they bring a consistent
|
|
|
|
performance in most cases.
|
|
|
|
|
|
|
|
However you may have specific cases that will need that you break the rules. In
|
|
|
|
that case the first thing to do is to **measure** using a profiler so that you
|
|
|
|
know where your problem are.
|
|
|
|
|
|
|
|
Then and only then you can decide to break the rules by using some mutable state
|
|
|
|
and/or custom `shouldComponentUpdate` implementation.
|
|
|
|
|
|
|
|
And remember to measure again after you did your changes, to check and prove
|
|
|
|
that your changes actually made an impact. Ideally you should always give links
|
|
|
|
to profiles when requesting a review for a performance patch.
|