Add React.PureComponent, inherit purity for functional components #6914

Open
wants to merge 2 commits into
from

Projects

None yet
@spicyj
Member
spicyj commented May 28, 2016 edited

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, but it also affects the behavior of functional children that are rendered:

Functional components

We've heard clearly that most React users intend for their functional components to be pure and to produce different output only if the component's props have changed (https://mobile.twitter.com/reactjs/status/736412808372314114). However, a significant fraction of users still rely on mutation in their apps; when used with mutation, comparing props on each render could lead to components not updating even when the data is changed.

Therefore, we're changing functional components to behave as pure when they're used inside a React.PureComponent but to rerender unconditionally when contained in a React.Component:

class Post extends React.PureComponent {  // or React.Component
  render() {
    return (
      <div className="post">
        <PostHeader model={this.props.model} />
        <PostBody model={this.props.model} />
      </div>
    );
  }
}

function PostHeader(props) {
  // ...
}

function PostBody(props) {
  // ...
}

In this example, the functional components PostHeader and PostBody will be treated as pure because they're rendered by a pure parent component (Post). If our app used mutable models instead, Post should extend React.Component, which would cause PostHeader and PostBody to rerender whenever Post does, even if the model object is the same.

We anticipate that this behavior will work well in real-world apps: if you use immutable data, your class-based components can extend React.PureComponent and your functional components will be pure too; if you use mutable data, your class-based components will extend React.Component and your functional components will update accordingly.

In the future, we might adjust these heuristics to improve performance. For example, we might do runtime detection of components like

function FancyButton(props) {
  return <Button style="fancy" text={props.text} />;
}

and optimize them to "inline" the child Button component and call it immediately, so that React doesn't need to store the props for Button nor allocate a backing instance for it -- causing less work to be performed and reducing GC pressure.

@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.
fdb84f4
@nfcampos
Contributor

Two questions:

  • for an app that consists entirely of functional components to opt in to pure functional components the root component needs to be a class component (which extends PureComponent), it can't be a functional component itself, right?
  • presumably every library that renders user-provided components — eg. react-router, react-redux, etc. — will need to provide two versions of whatever internal component renders the user provided components, one that extends PureComponent and one which extends Component so that library users can be offered the choice of having their functional components further down the tree be pure or not. I guess this is not really a question, more of a realisation that it looks like this will end up being an option in the api of a majority of libraries once this gets released.
@nfcampos
Contributor
nfcampos commented May 29, 2016 edited

I guess what I mean to say is this: this would definitely be a great idea if the creator of a component was also always in control of the parent of the component he created, since this is not true (eg. react-router is the one who renders your route components for you, not you), it might still be a great idea, but it is less so because it will effectively force every library that renders user components to expand its API so that users can get back control over how their components behave

@timdorr
timdorr commented May 29, 2016 edited

@nfcampos In react-router's case, given that we are pretty much always at the root of the render tree, having us be PureComponents won't really make much sense. I also don't think it's common to have functional components as your route components. I'm normally an advocate of our users setting up a dedicated route component that delegates any app activity to components above it. It serves as a good handing off point for route information and keeps separate of responsibilities high.

Edit: Also, react-redux does some really neat tricks to cache the rendered element and update only when things have actually changed. It's pretty neat and would eliminate some of the need to have a separate version that extends PureComponent.

Is there any support here for React.createClass components? Via a toggle property of some sort?

What would be interesting is the ability to swap out "pureness" at runtime, depending on certain conditions (for example, when an app might be trickling in a lot of data that doesn't get batched up). It looks like this is achievable via this.isPureReactComponent, but is this intended to be a public API?

@syranide
Contributor

Intuitively I kind of agree with @nfcampos on this. It seems to me that pureness isn't primarily a trait of the component, but the caller/usage of the component. It seems that you would have to expose one non-pure and one pure of every third-party component and that may even be complicated in more complex cases where there is a larger hierarchy being rendered internally.

Also, I'm curious, what's the behavior of <PureComponent><MyComponent /></PureComponent>, when elements are rendered as children, do they inherit pureness from the parent or the owner?

@satya164

Agree with @nfcampos. IMO it's better and easier to let the component decide if it's pure or not instead of the parent component doing it. In the long run, it might create confusion and unintended behaviour.

@satya164

I think something like MyComponent.shouldComponentUpdate = () => true or MyComponent.isPure = true can work.

@gaearon
Member
gaearon commented May 29, 2016 edited

for an app that consists entirely of functional components to opt in to pure functional components the root component needs to be a class component (which extends PureComponent), it can't be a functional component itself, right?

This is correct.

presumably every library that renders user-provided components — eg. react-router, react-redux, etc. — will need to provide two versions of whatever internal component renders the user provided components, one that extends PureComponent and one which extends Component so that library users can be offered the choice of having their functional components further down the tree be pure or not. I guess this is not really a question, more of a realisation that it looks like this will end up being an option in the api of a majority of libraries once this gets released.

In some cases, but not necessarily. This is not much different from today: libraries have to take a stance on where they are on the mutability spectrum. For example React Redux already has pure class containers, so it will switch to PureComponent. (But it already provides pure: false opt-out, so I guess we’ll have to keep offering that options.)

As for other libraries, it’s no different then the situation today. If you don’t want to force your users to be immutable, you can just export a regular class. If the consumer wants optimizations, they can wrap the children into their pure container:

<Router> // not optimized
  <MyApp> // my own, optimized! 
    <Header /> // functional, optimized thanks to MyApp

So I think

It seems that you would have to expose one non-pure and one pure of every third-party component

is unnecessary overkill, and doesn’t need to happen. If you’re not sure your users are immutable, just provide Component. It’s no different from choosing whether to provide shouldComponentUpdate() today—it’s exactly the same decision third party component have been doing for a long time.

I expect that most libraries will provide just Components, and if you want to opt into the optimizations, you just wrap your components in a PureComponent.

The optimizations kick in if the closest class parent is a PureComponent so, e.g. in case of React Router, you’d only need to make your App top-level handler a PureComponent, for the rest of the app to work.

In any case, there should be no goal of “making every component pure”. It’s just an optimization that would cover many cases, and generally benefit apps that use immutability. You shouldn’t be chasing that optimization with every component in your app—React reserves the right to not enable it in some cases anyway.

@glenjamin
Contributor

It's worth noting that in JS having the same arguments isn't enough to ensure purity.

function Since(props) {
return <p>{new Date() - props.date}</p>;
}

Treating a component like this as pure because of some property of the parent would not work.

I think for functional components to be marked as pure it'd have to be opt-in or opt-out per component, and for back-compat reasons that probably means opt-in.

@gaearon gaearon commented on the diff May 29, 2016
...rs/shared/stack/reconciler/ReactCompositeComponent.js
@@ -722,15 +728,21 @@ var ReactCompositeComponentMixin = {
this,
this._pendingElement,
transaction,
- this._context
+ this._context,
+ // Element updates are enqueued only at the top level, which we consider
+ // impure
+ false
@gaearon
gaearon May 29, 2016 Member

Maybe isParentPure? I didn’t realize it’s a boolean until this far.

@gaearon
Member
gaearon commented May 29, 2016

@glenjamin If this worked before, it only worked because some component above it caused it to re-render by setState or forceUpdate. Since this function wouldn’t have PureComponents above it, the existing pattern wouldn’t break, so I don’t see why this is breaking compatibility.

@gaearon
Member
gaearon commented May 29, 2016

To clarify again: this change doesn’t make all functional components pure. You still have to opt in by making their closest class parent a PureComponent. At this point we know that all children would be effectively pure anyway, because if the parent didn’t rerender, they wouldn’t either. This is why it’s safe to make them as pure in this situation.

@nfcampos
Contributor

It’s no different from choosing whether to provide shouldComponentUpdate() today—it’s exactly the same decision third party component have been doing for a long time.
Good point, I was under the impression that it was a different decision because functional children would be pure as well but I now see that it is not. 
 in case of React Router, you’d only need to make your App top-level handler a PureComponent, for the rest of the app to work.
that does indeed work.
In any case, there should be no goal of “making every component pure”. It’s just an optimization that would cover many cases, and generally benefit apps that use immutability. You shouldn’t be chasing that optimization with every component in your app—React reserves the right to not enable it in some cases anyway.
if you treat props as immutable in some places of your app, you probably need to do that everywhere or you'll end up incredibly confused as to which components assume immutability and which don't. in that case why not make every component in the app pure?

_____________________________

From: Dan Abramov notifications@github.com
Sent: Sunday, May 29, 2016 1:38 PM
Subject: Re: [facebook/react] Add React.PureComponent, inherit purity for functional components (#6914)
To: facebook/react react@noreply.github.com
Cc: Mention mention@noreply.github.com, Nuno Campos nuno.campos@me.com

for an app that consists entirely of functional components to opt in to pure functional components the root component needs to be a class component (which extends PureComponent), it can't be a functional component itself, right?

This is correct.

presumably every library that renders user-provided components — eg. react-router, react-redux, etc. — will need to provide two versions of whatever internal component renders the user provided components, one that extends PureComponent and one which extends Component so that library users can be offered the choice of having their functional components further down the tree be pure or not. I guess this is not really a question, more of a realisation that it looks like this will end up being an option in the api of a majority of libraries once this gets released.

In some cases, but not necessarily. This is not much different from today: libraries have to take a stance on where they are on the mutability spectrum. For example React Redux already has pure class containers, so it will switch to PureComponent. (But it already provides pure: false opt-out, so I guess we’ll have to switch there.)

As for other libraries, it’s no different then the situation today. If you don’t want to force your users to be immutable, you can just export a regular class. If the consumer wants optimizations, they can wrap the children into their pure container: // not optimized // my own, optimized!

// functional, optimized thanks to MyApp

So I think

It seems that you would have to expose one non-pure and one pure of every third-party component

is unnecessary overkill, and doesn’t need to happen. If you’re not sure your users are immutable, just provide Component. It’s no different from choosing whether to provide shouldComponentUpdate() today—it’s exactly the same decision third party component have been doing for a long time.

I expect that most libraries will provide just Components, and if you want to opt into the optimizations, you just wrap your components in a PureComponent.

The optimizations kick in if the closest class parent is a PureComponent so, e.g. in case of React Router, you’d only need to make your App top-level handler a PureComponent, for the rest of the app to work.

In any case, there should be no goal of “making every component pure”. It’s just an optimization that would cover many cases, and generally benefit apps that use immutability. You shouldn’t be chasing that optimization with every component in your app—React reserves the right to not enable it in some cases anyway.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@glenjamin
Contributor

If this worked before, it only worked because some component above it caused it to re-render by setState or forceUpdate. Since this function wouldn’t have PureComponents above it, the existing pattern wouldn’t break, so I don’t see why this is breaking compatibility.

To clarify again: this change doesn’t make all functional components pure. You still have to opt in by making their closest class parent a PureComponent. At this point we know that all children would be effectively pure anyway, because if the parent didn’t rerender, they wouldn’t either. This is why it’s safe to make them as pure in this situation.

I don't think this is quite the same thing as considering the functional components pure.

Adding an automatic shouldComponentUpdate would stop them updating when the parent re-renders. Inlining them into the parent would be equivalent to having the component always update.

These two actions are only equivalent if the component is actually referentially transparent - which is a property that cannot be known from outside in JS.

@gaearon
Member
gaearon commented May 29, 2016

if you treat props as immutable in some places of your app, you probably need to do that everywhere or you'll end up incredibly confused as to which components assume immutability and which don't. in that case why not make every component in the app pure?

Sure, but that wouldn’t work for people already using mutation (e.g. some implementations of Flux, some perf optimizations, making React work inside existing apps with Backbone, etc). So we want to keep that use case.

Adding an automatic shouldComponentUpdate would stop them updating when the parent re-renders. Inlining them into the parent would be equivalent to having the component always update.

I’m not sure I follow your point. There is no inlining in this PR (at least, not yet). I thought you were saying that adding this heuristic can potentially break functional components that use something like Date inside them. Can you show a bigger example demonstrating how they would break?

@glenjamin
Contributor

I thought you were saying that adding this heuristic can potentially break functional components that use something like Date inside them. Can you show a bigger example demonstrating how they would break?

I'm only on my phone at the mo, can do something a bit more concrete (although possibly contrived) later.

When a function component renders is entirely controlled by its parent, but what it renders with could be anything.

I think that:
Automatic shouldComponentUpdate would be a semantic change
Inlining functional components wouldn't be.

Here's a slightly contrived example that should demonstrate:

function LastRendered() {
  return <p>This component last rendered at {new Date().toString()}</p>;
}
@jquense
Contributor
jquense commented May 29, 2016

agree with @glenjamin here, having components act differently based on there parent seems a bit crazy, and that not the only thing determining pure essential of the component. I appreciate the attempts to optimize function components however this sort of heuristic feels really leaky.

as it is parent components have to sometimes be aware of their children types in order to attach refs, that leanings is less bad since refs are an escape hatch. With this we also have to be aware that changing a parent might cascade pure render checks down. thinking about libraries that wrap children to attach refs or refactoring a parent fn component to be statefull bc it's needed now.

@gaearon
Member
gaearon commented May 29, 2016

With this we also have to be aware that changing a parent might cascade pure render checks down.

How would that be noticeable? If you made the parent pure you already effectively short-circuited it. What happens below is just an additional optimization that React can make now; it doesn’t affect your behavior. Can you show an example demonstrating why this could be a problem?

@satya164
satya164 commented May 29, 2016 edited

@gaearon IMO the pureness of component should be up-to the component. A parent can not always know if a child component is pure or not.

For example, the following is always a pure component, regardless of its parents purity,

const Badge = ({ count }: { count: number }) => <span style={{ color: 'red' }}>{count}</span>;

The following is always impure,

const LastTime = () => <span>{Date.now()}</span>;

These are very bad examples, I know, but this demonstrates the fact that the child can be pure/impure regardless of its parent, and it should be up to the child to define its behaviour.

@gaearon
Member
gaearon commented May 29, 2016

I think some confusion comes from us giving another meaning to “pure” when we say “component”.

React never allowed render function to be impure. That is, components already have to have pure render function per the documentation:

The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not read from or write to the DOM or otherwise interact with the browser (e.g., by using setTimeout). If you need to interact with the browser, perform your work in componentDidMount() or the other lifecycle methods instead. Keeping render() pure makes server rendering more practical and makes components easier to think about.

So functions that use something like Date().now are not officially allowed anyway, and won’t even work unless by some coincidence a parent component redraws often enough.

In the context of this discussion, “pure component” means that not only it is a pure function (which it already must be anyway), but it also doesn’t rely on any deep mutations in the props. In other words, it means that the props are immutable rather than the function is pure. And whether the props are immutable or not, depends on the caller, and not on the component itself.

Sorry about the confusion.

@jquense
Contributor
jquense commented May 29, 2016

the examples above of non pure components with "pure" props all would/could change behavior depending on if the parent changes its component type. more so tho assuming that functional components that are the children of functional components are also eligible for pure render checks. you can easily create/break chains when refactoring components along the hierarchy no? That all feels like a bit of hard to track down magic that could case subtle bugs.

Not to mention the issues around context propagating through pure components

@gaearon
Member
gaearon commented May 29, 2016

(Of course React allows impure render functions in the sense that you can call forceUpdate() to force-render them. This doesn’t go away. You can also setState({}) to force re-rendering. In this case, the implementation proposed in PR would also disable the optimization.)

@gaearon
Member
gaearon commented May 29, 2016

you can easily create/break chains when refactoring components along the hierarchy no? That all feels like a bit of hard to track down magic that could case subtle bugs.

The idea is you shouldn’t have to think about it at all. This heuristic is intended to be an optimization React makes on its own; not something you need to be aware of. Because it doesn’t break the existing scenarios (does it?) it is effectively safe to add, so it’s a nice-to-have feature that you shouldn’t have to think about it.

Not to mention the issues around context propagating through pure components

It’s not any different than components with shouldComponentUpdate. Either implementing shouldComponentUpdate or inheriting PureComponent is an opt-in decision. Once you do so, you already break the context updates for all the children. So adding PureComponent and the optimizations related to it doesn’t make the situation any worse than it already is.

@gaearon
Member
gaearon commented May 29, 2016

could case subtle bugs.

Could you please provide a specific example that demonstrates how it causes a bug? It would need to show that adding PureContainer with the functional component optimization is more dangerous than adding shouldComponentUpdate (without functional optimization) in the same place.

Otherwise this discussion goes into a very theoretical tangent 😄 .

@syranide
Contributor

it’s exactly the same decision third party component have been doing for a long time.

@gaearon Not sure if that's a good argument considering it's an issue right now. :P But yeah, the rest of your argument seems sound. I guess in a sense you mark your "logic containers" as pure (because they deal with the data) and the children (being passed the data also inherits pureness) and it makes sense because they're mostly concerned with presentation only.

The optimizations kick in if the closest class parent is a PureComponent so, e.g. in case of React Router, you’d only need to make your App top-level handler a PureComponent, for the rest of the app to work.

Won't this break components that deal with mutable data internally? Even more so considering your example, ReactRouter must not mark itself as pure or my entire app must then be pure?

I may be missing something, but it seems like there should be three states; PURE, NON-PURE and INHERIT. So that you can go from being pure to being non-pure for instance, because you may be using some isolated component that may be internally non-pure and it must not inherit pureness. Or?

@gaearon
Member
gaearon commented May 29, 2016

Won't this break components that deal with mutable data internally?

I have an answer for this:

Could you please provide a specific example that demonstrates how it causes a bug?

😄

We’re getting too abstract and might mean some different things; code will help.

Even more so considering your example, ReactRouter must not mark itself as pure or my entire app must then be pure?

Not entire app, just the descendants between React Router and next impure class. React Router marking itself as pure would have the same effect as React Router implementing a strict shouldComponentUpdate. To cause re-renders below, you’d have to use setState() or forceUpdate(). But these work with the new model as well!

@gaearon
Member
gaearon commented May 29, 2016 edited

Functional components can’t setState() or forceUpdate(), right? So if functional component is inside a class component with strict shouldComponentUpdate() we know it won’t get re-rendered unless that parent also gets re-rendered. This is why we apply the heuristic here (and use PureComponent as a flag because we don’t know what’s in your shouldComponentUpdate()): functional component’s closest class parent is known to be pure.

If we introduce an impure class component between them, this will no longer be true. Maybe it calls setState({}) every second, and this causes functional component to update. This is why we don’t apply the heuristic here: functional component’s closest class parent is impure, so we don’t risk.

@jquense
Contributor
jquense commented May 29, 2016 edited

general bikeshedding aside. it strikes me that a change like this would benefit the most from benchmarking. Since this is all about performance anyway, a top criteria for acceptance probably should be "is the reasonably faster" for real world usages? Childless leaf nodes still run SCU? does that cost less than a reconciliation check? In the past I've only used SCU to gate updates on hot paths, so tips a good cost to benefit ratio. Is that still the case when many components, including simple ones, are doing SCU checks? (I don't know! but it seems like data would be helpful for this discussion.)

@syranide
Contributor
syranide commented May 29, 2016 edited

Won't this break components that deal with mutable data internally?

let someMutableObject = {value: 1};

class MyComp extends React.Component {
  construct() {
    this.state = {data: someMutableObject};
  }
  componentDidMount() {
    setInterval(() => {
      someMutableObject.value++;
      this.setState({
        data: someMutableObject
      });
    }, 100);
  }
  render() {
    return <div>{this.state.data.value}</div>
  }
}

@gaearon If I understand it all correctly, that works just fine today and is correct, but will break if some parent up the hierarchy is pure... right?

@gaearon
Member
gaearon commented May 29, 2016 edited

@syranide I’m not sure, why would it? The automatic optimization is only enabled for functional components, and this component is an (impure) class.

@glenjamin
Contributor

If I understand it all correctly, that works just fine today and is correct, but will break if some parent up the hierarchy is pure... right?

My understanding of the changes is that functional components get an automatic shallow-compare shouldComponentUpdate if their direct parent is a React.PureComponent.

Having thought about this a bit more, I reckon this should be pretty safe. It's pretty difficult to come up with a render() method which you are marking and implementing as pure and then somehow require a child to be impure.

Because this is opt-in via PureComponent and only affects direct children, consider me 👍

That said, here's a contrived example that works currently but would break if PureComponent was used instead of using shallowCompare on the parent class: [run on jsbin]

function createRenderCounter() {
  let n = 0;
  return function RenderCounter() {
    return <span>Rendered {++n} time(s)</span>;
  }
}

const shallowCompare = React.addons.shallowCompare;

class SillyComponent extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {items: [], smallItems: []};
  }
  more() {
    const x = Math.random();
    this.setState(s => ({
      items: s.items.concat([x]),
      smallItems: x < 0.3 ? s.smallItems.concat([x]) : s.smallItems
    }));
  }
  render() {
    return (
      <div>
        <p><a href="#" onClick={() => this.more()}>More</a></p>
        <div style={{width: "40%", float: "left"}}>
          All
        <ListOStuff items={this.state.items} />
        </div>
        <div style={{width: "40%", float: "right"}}>
          Small
        <ListOStuff items={this.state.smallItems} />
        </div>
      </div>
    )
  }
}

class ListOStuff extends React.Component {
  constructor(...args) {
    super(...args);
    this.rendercounter = createRenderCounter();
  }
  shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
  }
  render() {
    return (
      <ul>
      {this.props.items.map(i => <li>{i}</li>)}
      <li><this.rendercounter /></li>
      </ul>
    )
  }
}

React.render(<SillyComponent />, document.getElementById('app'));
@satya164

@gaearon I think some confusion comes from us giving another meaning to “pure” when we say “component”.

Oh, sorry, got confused in the second example.

Though my first example is still true. It's a pure component when all the props it accepts are primitives. If I have it inside as a child of an impure component, I can't skip the vdom diffing.

@glenjamin
Contributor

Though my first example is still true. It's a pure component when all the props it accepts are primitives. If I have it inside as a child of an impure component, I can't skip the vdom diffing.

The new behaviour only kicks in if the direct parent is explicitly marked as a PureComponent, so this shouldn't be a problem.

@satya164

@glenjamin Not really a problem, but there's no way to opt-in to the new behaviour for this component even if its pure.

@glenjamin
Contributor

@glenjamin Not really a problem, but there's no way to opt-in to the new behaviour for this component even if its pure.

You can opt in by inheriting from PureComponent, if you want the old behaviour you could implement shouldComponentUpdate via shallowCompare.

@satya164

@glenjamin I was talking about it being a pure functional component being direct child of an impure component. I'd have to change it to a class to make it pure.

@gaearon
Member
gaearon commented May 29, 2016

I think this proposal is not just about direct parents. It is about “the closest class parent”.

@aickin
Contributor
aickin commented May 29, 2016 edited

That said, here's a contrived example that works currently but would break if PureComponent was used instead of using shallowCompare on the parent class

I'm unclear how this would behave differently with this PR. Can you explain?

 return function RenderCounter() {
   return <span>Rendered {++n} time(s)</span>;
 }

This is an impure render function, which, as @gaearon notes above, has never been supported by React in any kind of component (functional, class, createClass, or factory). Will this example still show behavior difference with pure render functions?

@jquense
Contributor
jquense commented May 29, 2016 edited

Why add this behavior to React at all? it seems like the more conceptually correct way to do this is with a memoize HOF component. then each component can specify it's own behavior instead of it being continent on a parent which feels like a leaky sense of "component" anyway.

honestly my largest issue here is just that you need to coordinate parents of components in order to optimize it. that is a pain in the neck, components should not have to care what their parents are, that bit about function component refs is one of most frustrating bits of the current React API. having to refactoring children components because of changes in the parent (let alone grandparent or higher) is a pain :/ both as a library author and app builder

@markerikson

Conceptually, what's the difference between having a parent component explicitly extend React.PureComponent, and having a parent component just use a shallow-compare shouldComponentUpdate?

@glenjamin
Contributor

I think this proposal is not just about direct parents. It is about “the closest class parent”.

Ah ok, I can see how that would follow the same logic.

I'm unclear how this would behave differently with this PR. Can you explain?

With this PR, and using PureComponent for the ListOStuff, the counter would never increment.

This is an impure render function, which, as @gaearon notes above, has never been supported by React in any kind of component (functional, class, createClass, or factory). Will this example still show behavior difference with pure render functions?

This has always been recommended against, but there's never been anything which stopped you from grabbing values from elsewhere at render-time. If the functional component never uses anything other than it's props and state, there is no problem.

And to re-iterate, I'm 👍 for this change now that I understand that treating function components as pure is based on the purity of the closest-class-parent.

@jquense
Contributor
jquense commented May 29, 2016

cc @taion

@aickin
Contributor
aickin commented May 29, 2016

I'm very 👍 on this PR for a reason that hasn't been mentioned yet: server-side rendering performance. (I know: shocker that I'm interested in the SSR perf angle.)

I wrote an experimental feature in react-dom-stream that supports server-side component render caching, but it depends on a hacky function in your component that returns a cache key for the current props. I did that because there wasn't a way previously to declaratively know how a component's shouldComponentUpdate worked. With this PR, that would change, and the server renderer could know declaratively that a component (and its functional descendants) are pure.

So, I haven't thought about it deeply, but I think that this PR would enable the server renderer to performantly memoize pure components and their functional descendants, allowing for safe partial page caching. (It may or may not work for PureComponents that have an impure descendant; I need to think more about that case.) Given that for most sites, a huge portion of the page stays the same from render to render (think header, footer, standard buttons, etc.), this can result in a huge perf upgrade for server rendering.

@aickin
Contributor
aickin commented May 29, 2016

With this PR, and using PureComponent for the ListOStuff, the counter would never increment.

Oh yeah, I finally got my head around it. Half way through my morning coffee; sorry!

This has always been recommended against, but there's never been anything which stopped you from grabbing values from elsewhere at render-time.

I would quibble that it's stronger than recommended against. The docs say it's not allowed, and an impure render can randomly change behavior based on how the parent implements sCU. But that's a quibble, and I agree with you that you aren't stopped from doing it. You certainly can currently use impure render functions and it's possible you won't encounter the bugs that result.

And to re-iterate, I'm 👍 for this change now that I understand that treating function components as pure is based on the purity of the closest-class-parent.

👍

@aickin
Contributor
aickin commented May 29, 2016 edited

Conceptually, what's the difference between having a parent component explicitly extend React.PureComponent, and having a parent component just use a shallow-compare shouldComponentUpdate?

The difference AFAICT is that the functional component children are also treated as shallow-compare pure when you use React.PureComponent. Imagine the following component:

class Parent extends React.PureComponent {
  render() {
    return (
      <div>
        <Functional text={this.props.text[0]}/>
        <Functional text={this.props.text[1]}/>
      </div>
    );
}

function Functional(props) {
 return <div>{this.props.text}</div>;
}

ReactDOM.render(<Parent text={['foo', 'bar']}/>, domNode);
ReactDOM.render(<Parent text={['foo', 'qux']}/>, domNode);

With a shallow-compare shouldComponentUpdate parent, the entire tree would re-render on the second call to ReactDOM.render because the props are not shallow equal to the first render.

However, with PureComponent, the functional child components also get marked as pure, so the second call to render causes the parent and the second functional child to re-render, but not the first functional child. This is because the first functional child's props are shallow equal to the first render (the props are still just {text: 'foo'}).

After thinking about this a while, I think I found a bug in this PR having to do with context. If a functional child depends on context, it will get improperly marked as pure by a PureComponent parent/ancestor. I wrote a failing unit test on top of this PR that shows the problem.

(Edited because I realized I could simplify the test case a lot and reference a new commit.)

@jwbay
jwbay commented May 29, 2016 edited

honestly my largest issue here is just that you need to coordinate parents of components in order to optimize it. that is a pain in the neck, components should not have to care what their parents are [...] having to refactoring children components because of changes in the parent (let alone grandparent or higher) is a pain :/ both as a library author and app builder

Strongly agree with this assessment (emphasis mine). While there is precedence in a component influencing a grandchild in the form of context, it doesn't seem worth the mental gymnastics in this case. I really want pure functional components, but I value them being self-contained and easy to reason about more.

@gaearon
Member
gaearon commented May 29, 2016

What do you mean by mental gymnastics? You don't need to think about this optimization, it is an implementation detail of React. When you want to control optimizations you're welcome to keep using shouldComponentUpdate like before.

@gaearon
Member
gaearon commented May 29, 2016 edited

having to refactoring children components because of changes in the parent (let alone grandparent or higher) is a pain :/ both as a library author and app builder

Can you show a specific case where you'd have to refactor child components? I think you might have some mistaken assumptions.

Making something a pure component is pretty much equivalent to adding shouldComponentUpdate which has always been possible. In this sense nothing changes: yes, making parents pure (with any method) affects children. Why is this PR making it any worse in your opinion? I would love to see a specific example we could discuss.

@syranide
Contributor

I’m not sure, why would it? The automatic optimization is only enabled for functional components, and this component is an (impure) class.

@gaearon Ah ok, functional only. Now it makes sense. So then it's basically Component (NON-PURE), PureComponent (PURE) and functional component (INHERIT), right? Makes sense, but intuitively it seems like there there should be a way to specify a component that should inherit and not make it a feature exclusively for functional components. I would expect any internally pure third-party component to use inherit, but most aren't simple enough to be functional components.

@gaearon
Member
gaearon commented May 29, 2016

I would expect any internally pure third-party component to use inherit, but most aren't simple enough to be functional components.

But then it can make a decision that it’s PureComponent, can’t it? The proposition here is to limit this choice only to the places where you use setState() or can forceUpdate() (classes).

@syranide
Contributor
syranide commented May 29, 2016 edited

But then it can make a decision that it’s PureComponent, can’t it? The proposition here is to limit this choice only to the places where you use setState() or can forceUpdate() (classes).

No, because the props being passed by the parent may not be pure. EDIT: That's the entire point of functional components behaving this way right?

@gaearon
Member
gaearon commented May 29, 2016

Yeah, fair enough. I think that for now, it’s fine that heuristic doesn’t catch all the cases. This is not the goal. The goal is to speed up some of the common cases. And we’d have to benchmark it on the real products too to tell if it’s worth it. But I think this could be a good first step.

We’d have to think about our communication here because so far we asked users to implement special hooks to optimize performance. However now, if React includes some heuristics, people will feel obliged to understand their semantics. But the point is that there’s a lot of confusing stuff inside React that isn’t exposed to the user, and perhaps some of the optimizations move from the user space to React itself, so you won’t have to think about them as much.

@taion
taion commented May 29, 2016

The case that concerns me a bit is when e.g. I provide a container component in a library, something like:

<MyPureComponent>
  <LibraryContainerComponent>
    <MyFunctionalComponent />
  </LibraryContainerComponent>
</MyPureComponent>

Suppose this does some sort of relevant styling... like wraps everything in a panel or something... or like a toggle-able panel so it has to have some state, like the dock thing.

If I make <LibraryContainerComponent> a React.Component, then I opt my users out of the SCU optimization for <MyFunctionalComponent>. If I make it a React.PureComponent, then I might unwilling opt other users into that check.

So I think for libraries, there needs to be some sort of component that says "skip me and look at my parent" for determining purity.

@gaearon
Member
gaearon commented May 29, 2016

In other words, this heuristic would create a pressure on the third party component authors to either export functional or PureComponent components.

@taion
taion commented May 29, 2016

Is there an implicit long-term goal here to eventually deprecate the impure React.Component?

@aickin
Contributor
aickin commented May 29, 2016 edited

I think both @syranide and @taion are asking for a way to make es6 class components that behave like functional components do in this PR. I could totally imagine another component type, StatelessComponent, that removes setState, replaceState, forceUpdate, and componentWillReceiveProps from Component. StatelessComponents would behave the same as functional components, expect that they would get lifecycle calls and you could use refs with them. Also, in line with this PR, they would inherit purity from parents.

I think that's a bit of a premature feature at this point, though; it complicates the public facing API, and I'm not sure the use case is very compelling.

@markerikson

Semi-stupid question: is there any way this can be done without introducing a new separate component type? Like, an opt-in flag attached to the component class? MyComponent.isPure = true or something?

@aickin
Contributor
aickin commented May 29, 2016 edited

@markerikson AFAICT that's literally the only thing that React.PureComponent does, except that it calls it isPureReactComponent instead of isPure. So: yes! 😄

@markerikson

So why is a separate exported component class/type needed, then?

@aickin
Contributor
aickin commented May 29, 2016

So why is a separate exported component class/type needed, then?

It isn't! I'm pretty sure it's just the API that @spicyj chose to implement in this PR.

I like the separate component class myself. A Boolean flag API has some odd corner cases: for example, does it support mutation? What happens if midway through render you change it from true to false or vice versa?

I could also imagine a functional interface, which takes in a Component subclass and returns a Pure version of that subclass. That would keep the sense of it being a mixin rather than a superclass while still disallowing mutation. I don't feel strongly, though.

@stopachka

one thing to consider about the PR, is the path that will be taken onwards for further enhancing functional components.

for example, what if we wanted functional components to also have propTypes?

It would be nice to be able to do both types of enhancements in the same way.

for example, if there were some functions that react exposed ->

const HelloMessage = (props) => {...}

pure(HelloMesasge) // makes the functional component pure

or

withPropTypes(HelloMessage, {message: React.PropTypes.string}) // adds propTypes

this does become much more verbose, so I can see why it's not as tracktable an option. Worth considering though

@taion
taion commented May 29, 2016 edited

@aickin

That's not what I mean. What I mean is – suppose I'm writing a library. How should I define my components in a way that is least surprising to users? Right now, it's to use a non-functional components so things don't blow up on people who try to use refs.

The problem is that, with React.Component and React.PureComponent doing opposite things, there no longer is a "good" default that won't break in edge cases. What I mean is that I want to be able to write a <LibraryDiv> component in such a way that:

<A>
  <LibraryDiv>
    <B />
    <C />
  </LibraryDiv>
</A>

works identically to

<A>
  <div>
    <B />
    <C />
  </div>
</A>

in all cases (refs, SCU optimizations for B or C functional, &c.)

And this proposal will make that impossible.

@spicyj
Member
spicyj commented May 30, 2016

isPureReactComponent isn't intended to be public API (@timdorr). I should probably rename it.

@stopachka You can already use prop types with functional components by writing a static property:

function Button(props) {
  // ...
}
Button.propTypes = {
  label: React.PropTypes.string.isRequired,
};

But since we anticipate that purity will be the common case here, it makes sense to optimize it automatically without forcing people to add an extra flag. We might consider adding flags in the future to force purity or force impurity on functional components, but this PR starts with a smart default which gets us most of the way there.

@taion You're right that those are now slightly different. If you don't know whether people will pass impure children in and want to support that case, it's safest to use React.Component. Perhaps we could consider adding a way to make class components inherit purity in the future.

@gaearon
Member
gaearon commented May 30, 2016 edited

The problem is that, with React.Component and React.PureComponent doing opposite things, there no longer is a "good" default that won't break in edge cases.

Isn’t React.Component still a good default? It’s the same behavior as React used to have.

@aickin
Contributor
aickin commented May 30, 2016

works identically to in all cases (refs, SCU optimizations for B or C functional, &c.)

I think I understand you (thanks for the clarification; it helps!). I think that I would say that the functional component sCU optimizations are an optimization internal to the React renderer, not something that is exposed to the component author. So as @gaearon says, it's still OK for your library to export React.Component. The library won't be as efficient on re-render as a functional component would have been, but I would argue that it won't actually break. The content will always be rendered correctly.

That being said, I'm curious why you think that my proposal for a StatelessComponent class wouldn't fix your problem. I think it would be a good default for a library to export if you want the child functional components to have the same optimizations. Thoughts?

@jquense
Contributor
jquense commented May 30, 2016

@gaearon Component is a fine default, the point I think we are making is that since the parent is responsible for allowing the optimization of the child it's now harder for library authors to be good citizens to their users because we need to make choices about how their components can be optimized. to me it seems very strange to make the parent component responsible for turning on an optimization related to when a child component should rerender.

the solution that seems to allow the most freedom and least tension seems to me to allow individual function components to specify whether or not they are pure vs requiring that they be placed in pure parents.

PS. sorry if I'm not being as forthcoming with details or examples as you'd like I only have access to my phone this weekend so stuff is coming out a bit terse and stunted!

@jquense
Contributor
jquense commented May 30, 2016

pps: the HOC pattern is super popular for classy components why not provide an analog API via a memoize HOF. that seems more fp anyway no?

@joshduck
Contributor

Context altering the behaviour of my component is indistinguishable from global flags as far as my component is concerned. When creating a component I can't know with any certainty where my component will be rendered, let alone the implementation of my ancestors.

This is different from regular use cases for context because usually the component explicitly opts into non deterministic, hard to debug behavior.

I answered the poll in favor of mutable state. But I would prefer functional components be pure if it meant that they behaved consistently.

@spicyj
Member
spicyj commented May 30, 2016

@joshduck Can you give an example of a functional component that would work in one mode but break in the other?

@jimfb
Contributor
jimfb commented May 30, 2016

@joshduck I think that just means we need to fix context. Context is the thing that is broken. shouldComponentUpdate breaks context too. If a parent implements sCU, it affects components below it (even the ones using context).

@grahammendick

And whether the props are immutable or not, depends on the caller, and not on the component itself.

@gaearon Great explanation of why an ancestor can determine a descendant's purity. But doesn't that also mean that a PureComponent shouldn't determine its own purity? Can a PureComponent be impure?

The top level caller is ReactDOM.render, so shouldn't this have the ability to determine a component's purity, e.g., ReactDOM.render(<div />, el, pure)? This would allow for a fully functional component hierarchy.

@taion
taion commented May 30, 2016

I don't think React.Component is a fine default in general for component libraries.

To expand on that a bit, while I think the approaches outlined here are fine for React Router, @jquense (I think?) and I are speaking from our perspective as maintainers of React-Bootstrap, which I believe is the most-downloaded "dumb component" package in the React ecosystem.

On React-Bootstrap we regularly get complaints that our components don't implement pure render optimizations, even though they can (because all of them are pure). This is going to get a lot worse if this PureComponent > FunctionalComponent thing becomes a thing... because then not only do we fail to do pure render, we disable pure render optimizations for users as well.

Consider the component:

import React from 'react';
import Panel from 'react-bootstrap/lib/Panel';

class MyPureComponent extends React.PureComponent {
  /* ... */

  render() {
    return (
      <Panel defaultExpanded={false}>
        <MyFunctionalComponent data={this.props.data} />
      </Panel>
    );
  }
}

It's really weird that the pure-render-ness of <MyFunctionalComponent> depends on implementation details of <Panel>, and people are going to complain to us about this in a way where we can't give them any relief.

Is the concept of "owners" still a thing? Can this purity pass through owners instead of parents? I think that might actually be the least surprising way for this to work.

@aickin
Contributor
aickin commented May 30, 2016

I think that just means we need to fix context. Context is the thing that is broken.

@jimfb Interesting. Then do you have any opinions about what should be done about the failing context test case I submitted above?

@spicyj
Member
spicyj commented May 30, 2016

@taion Thanks, all good points. If your component's props are all scalar, it is safe to make it a PureComponent regardless of where it is rendered. If you have objects in props, Component is a better choice for now if you want to support that use case. You can always write a custom shouldComponentUpdate. I could imagine us adding a way to inherit purity later. You're also right that owners would be the more logical choice here but we're trying to get rid of them (in part because there are cases when they're not what you expect, like a ListView with a renderRow(i) callback called during ListView's render). "Wrapper" components are only a small fraction of components in our experience so we felt like it was okay to suggest using Component in those cases (unless you know your clients are pure, which people often would in their own apps).

@jimfb
Contributor
jimfb commented May 30, 2016

@jimfb Interesting. Then do you have any opinions about what should be done about the failing context test case I submitted above?

@aickin Yes, automatic subscriptions to the used context variables. But that's a personal opinion, not necessarily one shared by the rest of my team. See #2517

Is the concept of "owners" still a thing? Can this purity pass through owners instead of parents? I think that might actually be the least surprising way for this to work.

Yeah, I think that's a more-correct solution. Maybe we should look into making this work. Perhaps by passing createElement an implicit prop at transform time, like we do for __source.

@taion
taion commented May 30, 2016

The problem is that if I do that, I'm pretty sure I'm going to get endless issues from people asking why <MyFunctionalComponent> in the above example is re-rendering, even though <MyPureComponent> is a React.PureComponent.

The issue is not the props taken by <Panel> in that example – it's that the API accepts a children prop, and then the purity of functional components in that children node becomes a question of the purity of <Panel>.

This isn't an issue within an application, but if I have a public library that exports <Panel>, I can't make any assertions as to what my user is going to do, and I really would like to have some way to make <Panel> be "transparent" the way that <div> is (with respect to purity here).

@satya164
satya164 commented May 30, 2016 edited

@taion One way would be to force the user to manage the state itself instead of managing it inside your library. It also means giving more control to the user. Then you can have all your components as functional components. Though this is not really applicable to all libraries.

@taion
taion commented May 30, 2016

We offer both versions. We can't just use a functional component here, though, because then users wouldn't be able to attach a ref to our exported component.

@koba04
Contributor
koba04 commented May 31, 2016 edited

I wrote some tests for understanding clearly what PureComponent is.
koba04@e2eb5d6

#6914 (comment)
In this case, we need to add a Component wrapping MyFunctionalComponent for making it pure?

import React from 'react';
import Panel from 'react-bootstrap/lib/Panel';

class MyPureComponent extends React.PureComponent {
  /* ... */

  render() {
    return (
      <Panel defaultExpanded={false}>
        <PureContainer>   // makes MyFunctionalComponent pure
          <MyFunctionalComponent data={this.props.data} />
        </PureContainer>
      </Panel>
    );
  }
}
@taion
taion commented May 31, 2016

Doesn't work if there's more than one child.

@spicyj spicyj Inherit purity for functional components
We've heard clearly that most React users intend for their functional components to be pure and to produce different output only if the component's props have changed (https://mobile.twitter.com/reactjs/status/736412808372314114). However, a significant fraction of users still rely on mutation in their apps; when used with mutation, comparing props on each render could lead to components not updating even when the data is changed.

Therefore, we're changing functional components to behave as pure when they're used inside a React.PureComponent but to rerender unconditionally when contained in a React.Component:

```js
class Post extends React.PureComponent {  // or React.Component
  render() {
    return (
      <div className="post">
        <PostHeader model={this.props.model} />
        <PostBody model={this.props.model} />
      </div>
    );
  }
}

function PostHeader(props) {
  // ...
}

function PostBody(props) {
  // ...
}
```

In this example, the functional components PostHeader and PostBody will be treated as pure because they're rendered by a pure parent component (Post). If our app used mutable models instead, Post should extend React.Component, which would cause PostHeader and PostBody to rerender whenever Post does, even if the model object is the same.

We anticipate that this behavior will work well in real-world apps: if you use immutable data, your class-based components can extend React.PureComponent and your functional components will be pure too; if you use mutable data, your class-based components will extend React.Component and your functional components will update accordingly.

In the future, we might adjust these heuristics to improve performance. For example, we might do runtime detection of components like

```js
function FancyButton(props) {
  return <Button style="fancy" text={props.text} />;
}
```

and optimize them to "inline" the child Button component and call it immediately, so that React doesn't need to store the props for Button nor allocate a backing instance for it -- causing less work to be performed and reducing GC pressure.
0af8c44
@ghost
ghost commented Jun 1, 2016

@spicyj updated the pull request.

@spicyj
Member
spicyj commented Jun 1, 2016

Updated with isParentPure instead of pureParent.

@gaearon gaearon commented on the diff Jun 6, 2016
...rs/shared/stack/reconciler/ReactCompositeComponent.js
@@ -786,22 +818,32 @@ var ReactCompositeComponentMixin = {
var nextState = this._processPendingState(nextProps, nextContext);
var shouldUpdate = true;
- if (!this._pendingForceUpdate && inst.shouldComponentUpdate) {
- if (__DEV__) {
- if (this._debugID !== 0) {
- ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
- this._debugID,
- 'shouldComponentUpdate'
- );
+ var pureSelf =
@gaearon
gaearon Jun 6, 2016 Member

Similarly, could call this isSelfPure for clarity.

@chicoxyzzy
Contributor

FYI PureComponent may be unnecessary not only if you use mutation but also if you are sure you get new value each time.

Let's say we have WebSocket connection and backend does all checks to be sure it sends only new data to client. Then (using Redux, Flux or any other mutable or immutable approach) you change store (or whatever) or immidiately pass props to component. In that case any new props should trigger rerender immidiately and any shallowEqual checks will be redundant. Same for cases when one uses Rx and combineLatest.

@borisyankov
Contributor

I was looking forward to using this.
Is it still going to be merged, or some other approach being considered?

@gaearon
Member
gaearon commented Jun 30, 2016

Is it still going to be merged, or some other approach being considered?

Yes, this will be merged.

@taion
taion commented Jun 30, 2016

Are there any updates relating to #6914 (comment) and surrounding conversations?

Is the recommendation still that libraries offering container components should in general use Component instead of PureComponent for those container components?

@gaearon
Member
gaearon commented Jun 30, 2016

Is the recommendation still that libraries offering container components should in general use Component instead of PureComponent for those container components?

Yep, that’s the recommendation for now. We may adjust and extend the heuristic later—this shouldn’t stop us from adding PureComponent now.

@jimfb
Contributor
jimfb commented Jun 30, 2016 edited

@gaearon We need to solve this heuristic situation. We should not merge something with a broken heuristic. The bugs that this would introduce are too difficult to debug because the reason for the seemingly random failure is not visible by inspection and difficult to reproduce (unless you already know what you're looking for). We don't want people banging their heads against the wall for hours, trying to understand what went wrong.

We can merge PureComponent without the heuristic (figure out a heuristic later if we don't want to block PureComponent), or with a smarter heuristic that passes #6914 (comment) (like owner-based), but not with the current heuristic.

@spicyj
Member
spicyj commented Jun 30, 2016

with a smarter heuristic that passes #6914 (comment) (like owner-based)

Do you mean the context test linked there? You basically can't rely on context updates propagating anyway today so I don't think that is a problem.

@jimfb
Contributor
jimfb commented Jun 30, 2016

@spicyj Err, no, sorry, I meant:

class PureComponent extends React.PureComponent{
  render(){ return <div>{this.props.children}</div>}
}

function MyFunctionalInputComponent(props) {
  return <input type="text" value={props.data.text} onChange={props.data.handler} />
}

class Impure extends React.Component {
  constructor() {
    super();
    var data = {};
    data.text = "Jim";
    data.handler = (event) => { data.text = event.target.value; this.forceUpdate(); };
    this.state = {data: data};
  }
  render() {
    return <PureComponent name="foo">
      <MyFunctionalInputComponent data={this.state.data} />
    </PureComponent>
  }
}

ReactDOM.render(
  <Impure />,
  document.getElementById('container')
);

When writing this code, everything looks right. When I write my pure component, it looks pure, so that's good. I write my stateless functional component, it looks stateless and functional, so that seems good. I write my Impure component, it is impure, and that's good. When I put them together, it breaks, and I have no idea why.

The problem is that a user would never expect that the data being passed to MyFunctionalInputComponent is actually captured as a deeply nested prop of PureComponent, and is thus illegal. It feels like I'm following all the rules, because it feels like I'm only passing pure data to my pure component. My functional input component is just a function, and I expect it to behave like a function. Everything looks very reasonable. For an even moderately complex component, you'd never spot the bug. Even after I explain it to people, most people have trouble realizing what went wrong. The story is absolutely terrible.

@taion
taion commented Jun 30, 2016

@jimfb

The interesting case with your <PureComponent> above is that making it pure accomplishes almost nothing – the shallow comparison is always going to return "unequal" because of the children prop. If there were the concept of a component that passed through purity, without any particular SCU semantics, it would be just fine for that example.

Would it be reasonable to not apply the heuristic at all here for functional components? If I want to take advantage of the logic here, I'm going to have to change all of my components to extend PureComponent anyway. Is it that big a deal if I also have to do e.g. MyFunctionalComponent.pure = true? I already have to bind a bunch of static properties to those components like propTypes anyway.

Alternatively, a really conservative heuristic that just impure-ifies everything any non-pure component seems like it would avoid potential problems. As a library maintainer I'd just cut all of my library components over to PureComponent, so in practice I think things would be okay.

@martin-svk

This one is tricky.

Firstly, it seemed like a no big deal improvement. Just a shorter way to implement a SCU method with shallowCompare(this, newProps, newState). Many arguments pointing to a concept of owner should control it's own purity/rerending are invalid because we already have SCU method deciding if the child components (those not setting their own state) will be rerendered or not. Also, many examples were totally wrong, having impure render methods.

But the problem is, this PR takes this concept further and tries to apply some additional heuristics which can have unexpected causes. Like #6914 (comment) and #6914 (comment).

What I suggest is to just make all the functionall components behave like they had SCU implemented using shallowCompare on props, assuming users use immutable data as props. There will be a clear warning about this in docs. And people which want to do mutation can stick with classic Component's.

Additionally if PureComponent only did implement the SCU as I mentioned, I would not be against it.
Actually, that's what other people were already doing to save some typing, and what PureRenderMixin was all about, right?

@spicyj
Member
spicyj commented Jul 5, 2016

Gonna do the first half of this in #7195.

@ghost ghost added the CLA Signed label Jul 12, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment