Repository navigation
Refactor withViewportMatch to use useViewportMatch - #18950
jorgefilipecosta merged 2 commits into
Conversation
14c88fe to
28107c2
Compare
| const queriesResult = useViewPortQueriesResult(); | ||
| return ( | ||
| <WrappedComponent | ||
| { ...props } | ||
| { ...queriesResult } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Right, but withSelect wraps using a pure component, this HOC does not.
There was a problem hiding this comment.
Good point, I kept the previous behavior and also used pure HOC as the withSelect does.
| jest.mock( '@wordpress/compose', () => { | ||
| return { | ||
| ...jest.requireActual( '@wordpress/compose' ), | ||
| useViewportMatch( breakPoint, operator ) { |
There was a problem hiding this comment.
I wonder if we could mock this globally in all tests and have a utility to force its behavior
There was a problem hiding this comment.
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 ); |
There was a problem hiding this comment.
Interesting, I wonder if there are similar cases/discussions in the React community.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
764e7ff to
de5b7f6
Compare
|
This is a great candidate for a performance test comparison before/after |
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.