2 things about PureComponent you probably should know.
I hope that you heard that PureComponets, or pure-render-mixin in the past, or shouldComponentUpdate, can improve speed of your application.
And I hope that you heard the meanings of Pure
keyword in this context.
Pure function will evaluate the same results with the same arguments, and will not produce any side effects (or results MAY be NOT the same).
So pure
, pure by any reason and by any technique, component
will render
the same children
with the same props
.
That’s how 99.99% of people will understand PureComponent. As a predictable
function.
Let’s recall stateless
components, which assumed to be a pure function
.
But pure functions are not pure components.
Right now stateless component is always impure
component. As a result stateless components in many-many cases are slower that stateful components, cos you can optimize “stateful” components, but can’t change the way stateless ones works.
In the same time stateless components are more pure than PureComponents, as long they always do the same job, while PureComponent’s optimization behavior is not pure. Weird, right?
Stateless components (could be) SLOW!
But wait! One said that Facebook promised stateless components to be pure, but not now, may be tomorrow! Yes! Just wait a bit.
That was true for React 15. It is not true for 16
How PureComponents works?
React gives you the ability to optimise
component, by implementing shouldComponentUpdate
function.
Next, you can decide should you re-render it on props and state change, or not.
Pure-render-mixin or PureComponent just implements this function for you. And it is just impossible to generate it for a stateless component as long it is not a Component
. It is just a function.
Or possible? Actually, React uses that stateless component indirectly and it can perform necessary checks in Component Adapter
. Actually — stateless components are very complex thing, and React expects them to be pure functions. If not… oh, even don’t try.
So, the only question here might be a bit strange —
Why “
purifying"
of stateless components is....not enabled by default?
How Pure is a Pure component?
The answer lies in math.
if f(X) produces Y, then `f` will be `pure`, when with the same X `f` will generate the same Y.
With
absolutely
same X will produceabsolutely
same Y. Including a whole nesting component tree.
So, if you are a PureParent
you have to think shouldIUpdateMyself
, but also you have to think shouldIUpdateAllMyChildren
.
So, you have to be a good parent.
In some situations you can think
about direct children, but not for the ALL. Especially if you a just a component, and do not own some necessary knowledge.
And the second problem, that you should not only think about props
and state
— you also have to think about context
. And to think about context
you have to declare contextType, so you have to specialize that are you "thinking" about
. Imho, it is impossible.
As result, PureComponents will block ANY context changes (they don’t expect).
UPD: Ok, that was true for React 15 context. React 16 context API has nothing with it. It will always work. Always!
Let me write an example.
<MyApp> // just my app
<Router> // react-router.
<Chrome> // my _pure_ chrome. Just a chrome.
<Switch> // react-router Switch
<Route ....>
</Switch>
</Chrome>
</Router>
</MyApp>
Switch will not work. Haha!
React-router’s Router
will store current location in router
props of context
. And React-router’s Switch
will read it back and choose a Route
.
But Chrome
is a very pure component, and WILL NOT
react to context change in Router
, cos it isNOT USING
those values and should ignore them, cos has absolutely no way to read their values.
You have done well. Everything is correct. But it isn’t gonna work.
That’s why, then you use PureComponent you have to think not for a single component, but for a whole Application.
It is safe to use PureComponents as atoms
, ie small and final things like buttons. But it is not safe to use them in chromes, forms, pages and other molecules
.
“Not safe”, but allowed and absolutely ok. You have been warned.
Redux connect — is a PureComponent.
Yes — a very important thing, a HoC for a molecule is a pure one. And works even inside other pure components. And gets store
from a current context.
Same is working, for example, for styled-component
— you can wrap it with PureComponent, but it will still react to Theme changes.
Solution is simple — bypass logic, use old school
events bus, subcribe, wait and emit events.
Styled-componets:
componentWillMount() {
// subscribe to the event emitter. This
// is necessary due to pure components blocking
// context updates, this circumvents
// that by updating when an event is emitted.
const subscribe = this.context[CHANNEL];
this.unsubscribe = subscribe(nextTheme => { <----- MAGIC
React-redux:
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe =
this.store.subscribe(this.handleChange); <----- MAGIC
}
}componentDidMount() {
this.trySubscribe();
}
Thus, even if parent Pure Component will block
any update enables you to catch a change
, store update, context variable change, or everything else.
So — something inside
pure
components is verysoiled
and absolutelyimpure
. It is driven by side effects!
But this bypass straight logic flow
, and works just differently
from the rest of application.
So — just be careful. And don’t forget about magic
.
Aaaand….
And this is a reason, why any redux store update
will cause redraw
in each
connected component, and why you should use reselect just next to connect
HoC —
to stop unnecessary change propagation.
But you should read this from another point of view:
- redux-connect is a source of a change propagation.
- redux connect is the end of a change propagation. It is still a PureComponent.
And this leads to quite handy thing — you can control change propagation with redux-connect only. Just create a boundaries for a change. Lets talk about this in another article.
This leads to a simple proposion:
If you are not 100% sure that you have “rights” to stop propagation
— DONT STOP IT!!
For example — your children
could be defined inside another component(that is a common case!) and could use that component props, visible in their scope. But there is no way “you” could know it.
const App = (props) => (
<div>
<PureComponent>
<AnotherComponent>
<strong>{props.name}</strong>
</AnotherComponent>
</PureComponent>
</div>
);
In this case — “you” have no rights to be a PureComponent and stop updates, just anything could be placed inside, and might dont know — what.
This might become even more severe with render props, as long pure components could lead to different function scopes visible from different function-as-children, as long those functions were memoized(freezed) in the different (sometimes random) renders.
Conclusion
Pure components keep your application fast. Sometimes — more predictable, but often — less predictable
, as long they change the way application works. The way it flows
.
Stateless
components are not pure
, and may run slower than PureComponents by any kind. But usually this is not something you should think about. They are made from pure functions and they are predictable.
But… if you very wish to create a fast application with good user experience — you have to use Pure Component, or shouldComponentUpdate (sCU).
And the only way to use them — as a “event propagation boundaries”. Predictable “event horizons” in predictable places. To be the start of any update(as redux or Context API), and to be the end for anything “unexpected”. “Unpredicted”. “Unwanted”. “Unknown”.