Skip to content

Refactor withViewportMatch to use useViewportMatch - #18950

Merged
jorgefilipecosta merged 2 commits into
masterfrom
update/refactor-withViewportMatch-to-use-useViewportMatch
Dec 7, 2019
Merged

jorgefilipecosta merged 2 commits into
masterfrom
update/refactor-withViewportMatch-to-use-useViewportMatch

Conversation

@jorgefilipecosta

Copy link
Copy Markdown
Member

Description

This PR refactors withViewportMatch to use useViewportMatch. This allows us to implement a single simulation mechanism on the hook. And consolidates the different approaches we offer now.

cc: @youknowriad

How has this been tested?

I checked the unit tests pass.
I added an Image block I searched for the ImageEdit component, I verified isLargeViewPort is correctly passed by the HOC and that when I resize the window the prop changes.

@jorgefilipecosta jorgefilipecosta added [Type] Enhancement A suggestion for improvement. [Type] Code Quality Issues or PRs that relate to code quality labels Dec 5, 2019
@jorgefilipecosta
jorgefilipecosta force-pushed the update/refactor-withViewportMatch-to-use-useViewportMatch branch from 14c88fe to 28107c2 Compare December 5, 2019 20:17

@epiqueras epiqueras left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good.

Comment on lines +50 to +54
const queriesResult = useViewPortQueriesResult();
return (
<WrappedComponent
{ ...props }
{ ...queriesResult }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withSelect served as a memoization layer which this code doesn't replicate.

queriesResult will change on every render here. Could it be a performance concern?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @epiqueras, I don't think it is a concern, we are spreading queries result and passing each prop individually as a boolean e.g: isLarge: false, isSmall: true etc... Even if withViewportMatch is used in a pure component as we are just passing boolean values if useViewportMatch did not change we will not trigger unnecessary rerenders.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but withSelect wraps using a pure component, this HOC does not.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I kept the previous behavior and also used pure HOC as the withSelect does.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice.

jest.mock( '@wordpress/compose', () => {
return {
...jest.requireActual( '@wordpress/compose' ),
useViewportMatch( breakPoint, operator ) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could mock this globally in all tests and have a utility to force its behavior

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @youknowriad yes we can do that. I updated the PR to follow this approach and mocked the useViewportMatch hook globally.

// we are respecting that as from the static query of the HOC we generate
// a hook that calls other hooks always in the same order (because the query never changes).
// eslint-disable-next-line react-hooks/rules-of-hooks
return useViewportMatch( breakpointName, operator );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I wonder if there are similar cases/discussions in the React community.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @youknowriad,
I did some research on this. It seems it is possible to do this. The samples people referred to were things like defining an array of states const useInputs = [...Array(n)].map((_, i) => useState('name' + i)); and they should be avoided because normally there is a better alternative (in that case defined a single state with an array). In this specific case, I don't think we have an alternative (unless we change the hook API), and we are sure that the order the hooks are called will not change during the component life cycle, so it seems like something we could do.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally, this is symptomatic of a cluttered component tree and means that you should break down the caller into multiple child components, each with their own hook call, but here we are trying to maintain backwards compatibility.

An alternative would be to introduce a useViewportMatches hook, but it's not a big deal. We should just clearly document that the query has to be static.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we use this mechanism just to maintain backward compatibility. Given the way an HOC works the query will always be static for a component instance, there is no way of doing the opposite. Changing the query will require a call to the HOC and a new component is created.
I don't think we should change the hook API so I guess we can keep this solution.

@jorgefilipecosta
jorgefilipecosta force-pushed the update/refactor-withViewportMatch-to-use-useViewportMatch branch from 764e7ff to de5b7f6 Compare December 6, 2019 19:23
@jorgefilipecosta
jorgefilipecosta merged commit db52a91 into master Dec 7, 2019
@jorgefilipecosta
jorgefilipecosta deleted the update/refactor-withViewportMatch-to-use-useViewportMatch branch December 7, 2019 10:59
@youknowriad youknowriad added this to the Gutenberg 7.1 milestone Dec 9, 2019
@youknowriad

Copy link
Copy Markdown
Contributor

This is a great candidate for a performance test comparison before/after

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

Labels

[Type] Code Quality Issues or PRs that relate to code quality [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants