-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Try adding a pure Higher Order Component #6313
Conversation
33c838e
to
1f428d3
Compare
Would using |
@atimmer Not certain what's the difference between the two implementation, do you know more about it? I kind of like the HoC approach, where you can use it without transforming your component into a class. Also, I don't think it's possible to use React.PureComponent the way I'm implementing |
From https://reactjs.org/docs/react-api.html#reactpurecomponent:
Indeed it's interesting how you handle
Not sure about this part. I would expect children components to re-render when they are wrapped with data HOCs anyway. |
@youknowriad can we add some tests once we agree that it is the way to go to make sure that |
Minor: have you considered naming it
|
I generally approve this. I agree tests, esp. for regressions, are in order. Further: although making some critical components pure should help us a lot, there's the caveat that this HoC may be abused in the future if not applied judiciously (like other kinds of premature or uninformed optimization). Short of improving HoC documentation, I don't really know what the solution for that is. :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also prefer the name pure
.
data/index.js
Outdated
@@ -242,7 +242,7 @@ export function dispatch( reducerKey ) { | |||
* @return {Component} Enhanced component with merged state data props. | |||
*/ | |||
export const withSelect = ( mapStateToProps ) => createHigherOrderComponent( ( WrappedComponent ) => { | |||
return class ComponentWithSelect extends Component { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be expressed as:
createHigherOrderComponent( compose( [
purify,
( WrappedComponent ) => class extends Component {
}
] ), 'withSelect' );
element/index.js
Outdated
* | ||
* @return {WPComponent} Component class with generated display name assigned. | ||
*/ | ||
export const purify = createHigherOrderComponent( ( Wrapped ) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you choose to export this in element
, and not components
?
FWIW, I had a branch with similar change, and encountered that data
would then depend on components
, which surfaced yet another issue of: Should withSelect
and withDispatch
be in data
or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I asked myself several times about withFilters
in components
which depends on hooks
. A very similar case.
Why did you choose to export this in element, and not components ?
I'm sure this is all about dependencies. There needs to be a solution :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually didn't think that much about it. I put it under element to match React
:) which also provides a pure component definition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well explained, I'm sold :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, ignoring the fact that React does it, I don't see why, for example, we'd want to export a pure
higher-order component in element
, but ifCondition
higher-order component in components
. What judgement do we use to categorize them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pure
, ifCondition
, withState
, withInstanceId
, but also createHigherOrderComponent
and compose
- we can put all of them together in their own module 💯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alternative perspective is that components is a baseline, and where we're encountering circular dependencies is a sign where we have our dependencies inversed. Considering viewport
and the issues highlighted in #5316, the current dependency hierarchy is:
Viewport (
ifViewportMatches
) > Components (ifCondition
)
(This is problematic in #5316 in introducing the reverse dependency from Popover to Viewport)
Where maybe it ought to be:
Components (
ifViewportMatches
+ifCondition
) > Viewport (withSelect( 'viewport' )
)
This highlights a third type of component: Data-bound components.
We could treat them all separately, e.g. higher-order-components
, app-components
, ui-components
, though it's not clear to me whether this will ultimately solve all of our issues, and of course has added overhead in making determinations of which components / utilities belong where.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So did we just not decide anything here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed it's going to be renamed to pure
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was ... I guess you are referring to the location of this HOC, we need to come up with something sooner than later 👍
|
||
return class extends Component { | ||
shouldComponentUpdate( nextProps ) { | ||
return ! isShallowEqual( nextProps, this.props ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting conclusion that we don't need to compare state here, and I suppose it makes sense since it's wrapping the original (non-class) component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed on this one, there is no state in functional components 👍
element/test/index.js
Outdated
return <p>{ ++i }</p>; | ||
} ); | ||
const wrapper = mount( <MyComp /> ); | ||
wrapper.update(); // Upading don't rerender |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in the comment (also in line 246).
How this wrapper.update()
works, it's super confusing ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well I wanted to rerender the component without passing new props or something. But it's not that important since passing a similar prop is also tested
f2f471e
to
67e17f6
Compare
I would love to see this PR merged later this week. I guess the only remaining issue is where this new functionality should be located as discussed here: #6313 (comment). Otherwise, it has big 👍 from me :) |
I've published |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎸
@aduth Are you planning to update this PR or use |
@youknowriad In the process of integrating, I found a bug which led to WordPress/packages#116 . We could merge that and bring it in here, or do separately. I'd be fine to do separately as well. |
This PR adds a purify Higher Order Component used to wrap
withSelect
andwithDispatch
components (for now) to make them pure.Pure components mean they don't rerender unless their props or state changed (shallow equality).
Testing instructions
Notes