Skip to content
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

Refactor withViewportMatch to use useViewportMatch #18950

Conversation

@jorgefilipecosta
Copy link
Member

jorgefilipecosta commented Dec 5, 2019

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 force-pushed the update/refactor-withViewportMatch-to-use-useViewportMatch branch from 14c88fe to 28107c2 Dec 5, 2019
Copy link
Contributor

epiqueras left a comment

Looking good.

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

This comment has been minimized.

Copy link
@epiqueras

epiqueras Dec 5, 2019

Contributor

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?

This comment has been minimized.

Copy link
@jorgefilipecosta

jorgefilipecosta Dec 6, 2019

Author Member

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.

This comment has been minimized.

Copy link
@epiqueras

epiqueras Dec 6, 2019

Contributor

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

This comment has been minimized.

Copy link
@jorgefilipecosta

jorgefilipecosta Dec 6, 2019

Author Member

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

This comment has been minimized.

Copy link
@epiqueras

epiqueras Dec 6, 2019

Contributor

Nice.

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

This comment has been minimized.

Copy link
@youknowriad

youknowriad Dec 6, 2019

Contributor

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

This comment has been minimized.

Copy link
@jorgefilipecosta

jorgefilipecosta Dec 6, 2019

Author Member

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 );

This comment has been minimized.

Copy link
@youknowriad

youknowriad Dec 6, 2019

Contributor

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

This comment has been minimized.

Copy link
@jorgefilipecosta

jorgefilipecosta Dec 6, 2019

Author Member

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.

This comment has been minimized.

Copy link
@epiqueras

epiqueras Dec 6, 2019

Contributor

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.

This comment has been minimized.

Copy link
@jorgefilipecosta

jorgefilipecosta Dec 6, 2019

Author Member

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 Dec 6, 2019
@jorgefilipecosta jorgefilipecosta merged commit db52a91 into master Dec 7, 2019
2 checks passed
2 checks passed
pull-request-automation
Details
Travis CI - Pull Request Build Passed
Details
@jorgefilipecosta jorgefilipecosta deleted the update/refactor-withViewportMatch-to-use-useViewportMatch branch Dec 7, 2019
@youknowriad youknowriad added this to the Gutenberg 7.1 milestone Dec 9, 2019
@youknowriad

This comment has been minimized.

Copy link
Contributor

youknowriad commented Dec 9, 2019

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
Projects
None yet
3 participants
You can’t perform that action at this time.