Add React.PureComponent #7195

Merged
merged 1 commit into from Jul 6, 2016

Projects

None yet
@spicyj
Member
spicyj commented Jul 5, 2016

This provides an easy way to indicate that components should only rerender when given new props, like PureRenderMixin. If you rely on mutation in your React components, you can continue to use React.Component.

Inheriting from React.PureComponent indicates to React that your component doesn't need to rerender when the props are unchanged. We'll compare the old and new props before each render and short-circuit if they're unchanged. It's like an automatic shouldComponentUpdate.

@gaearon gaearon commented on the diff Jul 5, 2016
...rs/shared/stack/reconciler/ReactCompositeComponent.js
- if (__DEV__) {
- if (this._debugID !== 0) {
- ReactInstrumentation.debugTool.onEndLifeCycleTimer(
- this._debugID,
- 'shouldComponentUpdate'
- );
+ shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
+ if (__DEV__) {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(
+ this._debugID,
+ 'shouldComponentUpdate'
+ );
+ }
+ }
+ } else {
@gaearon
gaearon Jul 5, 2016 Member

Nit: could be else if for less nesting?

@spicyj
spicyj Jul 5, 2016 Member

It could be but I prefer parallel structure in my if/else-if cases so I left it like this.

@gaearon gaearon commented on the diff Jul 5, 2016
...rs/shared/stack/reconciler/ReactCompositeComponent.js
- 'shouldComponentUpdate'
- );
+ shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
+ if (__DEV__) {
+ if (this._debugID !== 0) {
+ ReactInstrumentation.debugTool.onEndLifeCycleTimer(
+ this._debugID,
+ 'shouldComponentUpdate'
+ );
+ }
+ }
+ } else {
+ if (this._compositeType === CompositeTypes.PureClass) {
+ shouldUpdate =
+ !shallowEqual(prevProps, nextProps) ||
+ !shallowEqual(inst.state, nextState);
@gaearon
gaearon Jul 5, 2016 Member

Did you abandon the idea of comparing state by reference?

@spicyj
spicyj Jul 5, 2016 edited Member

For now. There's a bunch of our internal components that rely on it so probably externally too. (FB-only link: https://fburl.com/373836543.)

@morenoh149
morenoh149 Aug 8, 2016 Contributor

Do you have to be a fb employee to see the internal link?

@spicyj
spicyj Aug 8, 2016 Member

Yes, that's what I meant by "internal".

@syranide
Contributor
syranide commented Jul 6, 2016

When documenting this I'd say it's important to note that callback handlers that are bound in render will always fail shallowEqual, many people are unaware of this and there has previously been discussions about ignoring functions in the comparison (I know this was recommended at some point).

@jimfb
Contributor
jimfb commented Jul 6, 2016

In addition to event handlers bound in render, it is probably also worth mentioning that any component which takes jsx children will also always fail shallowEqual, since the children elements will be re-created for every render. I think most people don't realize this.

@sebmarkbage sebmarkbage and 1 other commented on an outdated diff Jul 6, 2016
src/isomorphic/modern/class/ReactPureComponent.js
+ *
+ * @providesModule ReactPureComponent
+ */
+
+'use strict';
+
+var ReactComponent = require('ReactComponent');
+
+/**
+ * Base class helpers for the updating state of a component.
+ */
+function ReactPureComponent(props, context, updater) {
+ ReactComponent.call(this, props, context, updater);
+}
+
+Object.assign(ReactPureComponent.prototype, ReactComponent.prototype);
@sebmarkbage
sebmarkbage Jul 6, 2016 Member

Should we extend ReactComponent? What about people doing instanceof React.Component even though they shouldn't?

@spicyj
spicyj Jul 6, 2016 Member

All right.

@vjeux vjeux and 2 others commented on an outdated diff Jul 6, 2016
src/isomorphic/modern/class/ReactPureComponent.js
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ReactPureComponent
+ */
+
+'use strict';
+
+var ReactComponent = require('ReactComponent');
+
+/**
+ * Base class helpers for the updating state of a component.
+ */
+function ReactPureComponent(props, context, updater) {
+ ReactComponent.call(this, props, context, updater);
@vjeux
vjeux Jul 6, 2016 Contributor

nit: should you return the value of the call?

@spicyj
spicyj Jul 6, 2016 Member

Doesn't really matter since our constructor doesn't return anything.

@sebmarkbage
sebmarkbage Jul 6, 2016 edited Member

You might want to consider just inlining ReactComponent's constructor code here since it is a hot path and an inliner (AOT nor JIT) probably won't cover this case.

@spicyj
spicyj Jul 6, 2016 Member

All right.

@spicyj spicyj Add React.PureComponent
This provides an easy way to indicate that components should only rerender when given new props, like PureRenderMixin. If you rely on mutation in your React components, you can continue to use `React.Component`.

Inheriting from `React.PureComponent` indicates to React that your component doesn't need to rerender when the props are unchanged. We'll compare the old and new props before each render and short-circuit if they're unchanged. It's like an automatic shouldComponentUpdate.
aab1fd6
@spicyj spicyj merged commit c8fbdac into facebook:master Jul 6, 2016
@STRML
Contributor
STRML commented Jul 7, 2016

@jimfb This is mitigated somewhat in production by the babel constant elements transform, no?

@zpao zpao added this to the 15-next milestone Jul 7, 2016
@zpao zpao added the semver-minor label Jul 7, 2016
@usmanajmal usmanajmal added a commit to usmanajmal/react that referenced this pull request Jul 11, 2016
@spicyj @usmanajmal spicyj + usmanajmal Add React.PureComponent (#7195)
This provides an easy way to indicate that components should only rerender when given new props, like PureRenderMixin. If you rely on mutation in your React components, you can continue to use `React.Component`.

Inheriting from `React.PureComponent` indicates to React that your component doesn't need to rerender when the props are unchanged. We'll compare the old and new props before each render and short-circuit if they're unchanged. It's like an automatic shouldComponentUpdate.
a3826bb
@zpao zpao modified the milestone: 15-next, 15.3.0 Jul 13, 2016
@zpao zpao added a commit that referenced this pull request Jul 13, 2016
@spicyj @zpao spicyj + zpao Add React.PureComponent (#7195)
This provides an easy way to indicate that components should only rerender when given new props, like PureRenderMixin. If you rely on mutation in your React components, you can continue to use `React.Component`.

Inheriting from `React.PureComponent` indicates to React that your component doesn't need to rerender when the props are unchanged. We'll compare the old and new props before each render and short-circuit if they're unchanged. It's like an automatic shouldComponentUpdate.
(cherry picked from commit c8fbdac)
cf07f0c
@vlucas
vlucas commented Jul 14, 2016

Is there also going to be a React.createClass({ ... }) way to do this?

@gaearon
Member
gaearon commented Jul 14, 2016

@vlucas

For React.createClass(), you can either use PureRenderMixin or shallowCompare (both are available as addons).

@developit

@gaearon / @spicyj - are you guys able to confirm this will land in 15.x with the shallowEqual() approach taken in this PR?

@spicyj
Member
spicyj commented Jul 15, 2016

That's the plan.

@developit

Great, thanks.

@slmgc
slmgc commented Jul 15, 2016

@spicyj don't you think ReactPureComponent and isPureReactComponent look a little bit inconsistent? Could isReactPureComponent be a better name?

@sebmarkbage
Member

Actually, that's a good point. We should make it a worse name so that nobody relies on this implementation detail. :)

@brycehanscomb

Why was the decision to implement this class favoured over making React better equipped to deal with functional stateless components?

@Kovensky

@brycehanscomb I believe the intention is to add this as a non-breaking opt-in change, and then in React 16 change the internal SFC wrapper class to derive from React.PureComponent. I am just guessing, though.

@gaearon
Member
gaearon commented Jul 30, 2016

It's a stepping stone. We'll keep thinking about optimizing functional stateless components.

However just making every one of them work like PureComponent can slow down your app. Please don't think shallow equality checks are extremely cheap. They can help your app when placed strategically but just making every single component pure can actually make your app slower. Tradeoffs.

For now we added a base class because we wanted an official way of marking component as compatible with shallow equality checks, without using mixins. We'll see how and if we want to extend this behavior in some cases to functional components but it's not as straightforward as just making them behave the same.

@Kovensky Kovensky referenced this pull request in DefinitelyTyped/DefinitelyTyped Jul 30, 2016
Merged

Add PureComponent definition (React 15.3) #10377

@Andarist
Contributor

I remember from the other discussion thread that SFC were supposed to check the nearest class parent and based on it being Pure or not should opt in the being pure themselves.

Is it the part of this release?

@gaearon
Member
gaearon commented Jul 30, 2016

No, we decided to not proceed with this because the community discussion around it was controversial. These heuristics would be far from obvious, and we feel that this is not a good time to introduce them. Maybe we’ll figure out something more explicit in the future.

@mattkrick mattkrick referenced this pull request in ParabolInc/action Jul 30, 2016
Closed

Upgrade to React 15.3 #132

@raphaguasta

Hi @gaearon!

In the Changelog of react 15.3 (https://github.com/facebook/react/blob/v15.3.0/CHANGELOG.md), says that React.PureComponent was added, and refers this discussion. But here you said that this feature didn't was incorporated.

After all, it's in the React 15.3 or not? Is there some documentation about this?

Thanks :)

@spicyj
Member
spicyj commented Aug 23, 2016

PureComponent is included; the heuristics for stateless functional components are not.

@gaearon
Member
gaearon commented Aug 23, 2016

The feature is incorporated. My comment referred specifically to the previous comment: #7195 (comment).

I remember from the other discussion thread that SFC were supposed to check the nearest class parent and based on it being Pure or not should opt in the being pure themselves.

So “heuristics” for functional components are not in, but PureComponent is.

@raphaguasta

Great! Thanks guys! :)

@mmcgahan

Is there a plan to add docs to https://facebook.github.io/react/docs about PureComponent?

I'm running into some unexpected SyntaxError: Unexpected token import errors when running my Jest unit tests on a component that just changed from class Avatar extends React.Component to class Avatar extends React.PureComponent - no other code was touched but I'm not sure where to tweak my configuration without reference docs.

@koba04
Contributor
koba04 commented Aug 24, 2016

@mmcgahan #7379

@gaearon
Member
gaearon commented Aug 24, 2016

I'm running into some unexpected SyntaxError: Unexpected token import errors when running my Jest unit tests on a component that just changed from class Avatar extends React.Component to class Avatar extends React.PureComponent - no other code was touched but I'm not sure where to tweak my configuration without reference docs.

This seems like something you’d want to file with Jest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment