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

Add useQuerySelect to core/data #38134

Closed
wants to merge 14 commits into from
Closed

Conversation

adamziel
Copy link
Contributor

@adamziel adamziel commented Jan 21, 2022

Description

This PR proposes a new hook called useResolveSelect. It is used like this:

const { data, isResolving, hasFinished } = useResolveSelect(
	( resolve ) => resolve( coreStore ).getEntityRecord( 'postType', 'page', pageId ),
	[ pageId ]
};

The rationale is to make snippets like below more readable and faster to write – I find myself looking up the correct way to work with resolution status every time:

const { data, isResolving, hasFinished } = useSelect(
	( select ) => {
		const {
			getEntityRecord,
			isResolving,
			hasFinishedResolution
		} = select( coreStore );
		const args = [ 'postType', 'page', pageId ];
		return {
			data: getEntityRecord( ...args ),
			isResolving: isResolving( 'getEntityRecord', args ),
			hasFinished: hasFinishedResolution( 'getEntityRecord', args ),
		}
	},
	[ pageId ]
};

In #38135 I also drafted a higher-level hook called useEntityRecord building on top of this PR:

const { data, isResolving, hasFinished } = useEntityRecord( 'postType', 'page', pageId );

@youknowriad's work on the suspense API support in #37261 is related, but I see it as a separate API that doesn't diminish the value of having useResolveSelect.

Testing steps

This is a new hook so there's not much to test in the UI. Here's an alternative test plan:

  1. Confirm the code makes sense
  2. Confirm the unit tests pass and make sense
  3. Read the included documentation and examples and confirm they make sense

cc @kevin940726 @talldan @gziolo @draganescu @ellatrix @noisysocks @jsnajdr @getdave

@adamziel adamziel added the [Package] Data /packages/data label Jan 21, 2022
@adamziel adamziel self-assigned this Jan 21, 2022
@github-actions
Copy link

github-actions bot commented Jan 21, 2022

Size Change: +544 B (0%)

Total Size: 1.14 MB

Filename Size Change
build/block-editor/index.min.js 142 kB +8 B (0%)
build/components/index.min.js 215 kB +3 B (0%)
build/data/index.min.js 7.84 kB +400 B (+5%) 🔍
build/edit-post/index.min.js 29.9 kB +134 B (0%)
build/edit-post/style-rtl.css 7.17 kB +23 B (0%)
build/edit-post/style.css 7.16 kB +22 B (0%)
build/edit-site/index.min.js 41.6 kB +2 B (0%)
build/editor/index.min.js 38.4 kB -48 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 960 B
build/admin-manifest/index.min.js 1.1 kB
build/annotations/index.min.js 2.75 kB
build/api-fetch/index.min.js 2.22 kB
build/autop/index.min.js 2.12 kB
build/blob/index.min.js 459 B
build/block-directory/index.min.js 6.28 kB
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/default-editor-styles-rtl.css 378 B
build/block-editor/default-editor-styles.css 378 B
build/block-editor/style-rtl.css 14.8 kB
build/block-editor/style.css 14.8 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 65 B
build/block-library/blocks/archives/style.css 65 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 111 B
build/block-library/blocks/audio/style.css 111 B
build/block-library/blocks/audio/theme-rtl.css 125 B
build/block-library/blocks/audio/theme.css 125 B
build/block-library/blocks/block/editor-rtl.css 161 B
build/block-library/blocks/block/editor.css 161 B
build/block-library/blocks/button/editor-rtl.css 470 B
build/block-library/blocks/button/editor.css 470 B
build/block-library/blocks/button/style-rtl.css 560 B
build/block-library/blocks/button/style.css 560 B
build/block-library/blocks/buttons/editor-rtl.css 292 B
build/block-library/blocks/buttons/editor.css 292 B
build/block-library/blocks/buttons/style-rtl.css 275 B
build/block-library/blocks/buttons/style.css 275 B
build/block-library/blocks/calendar/style-rtl.css 207 B
build/block-library/blocks/calendar/style.css 207 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 79 B
build/block-library/blocks/categories/style.css 79 B
build/block-library/blocks/code/style-rtl.css 90 B
build/block-library/blocks/code/style.css 90 B
build/block-library/blocks/code/theme-rtl.css 131 B
build/block-library/blocks/code/theme.css 131 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 406 B
build/block-library/blocks/columns/style.css 406 B
build/block-library/blocks/comment-template/style-rtl.css 127 B
build/block-library/blocks/comment-template/style.css 127 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-query-loop/editor-rtl.css 95 B
build/block-library/blocks/comments-query-loop/editor.css 95 B
build/block-library/blocks/cover/editor-rtl.css 546 B
build/block-library/blocks/cover/editor.css 547 B
build/block-library/blocks/cover/style-rtl.css 1.4 kB
build/block-library/blocks/cover/style.css 1.4 kB
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 417 B
build/block-library/blocks/embed/style.css 417 B
build/block-library/blocks/embed/theme-rtl.css 124 B
build/block-library/blocks/embed/theme.css 124 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 255 B
build/block-library/blocks/file/style.css 255 B
build/block-library/blocks/file/view.min.js 322 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 965 B
build/block-library/blocks/gallery/editor.css 967 B
build/block-library/blocks/gallery/style-rtl.css 1.61 kB
build/block-library/blocks/gallery/style.css 1.61 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 159 B
build/block-library/blocks/group/editor.css 159 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 114 B
build/block-library/blocks/heading/style.css 114 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 810 B
build/block-library/blocks/image/editor.css 809 B
build/block-library/blocks/image/style-rtl.css 500 B
build/block-library/blocks/image/style.css 503 B
build/block-library/blocks/image/theme-rtl.css 124 B
build/block-library/blocks/image/theme.css 124 B
build/block-library/blocks/latest-comments/style-rtl.css 284 B
build/block-library/blocks/latest-comments/style.css 284 B
build/block-library/blocks/latest-posts/editor-rtl.css 199 B
build/block-library/blocks/latest-posts/editor.css 198 B
build/block-library/blocks/latest-posts/style-rtl.css 447 B
build/block-library/blocks/latest-posts/style.css 446 B
build/block-library/blocks/list/style-rtl.css 94 B
build/block-library/blocks/list/style.css 94 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 493 B
build/block-library/blocks/media-text/style.css 490 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 649 B
build/block-library/blocks/navigation-link/editor.css 650 B
build/block-library/blocks/navigation-link/style-rtl.css 94 B
build/block-library/blocks/navigation-link/style.css 94 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation-submenu/view.min.js 343 B
build/block-library/blocks/navigation/editor-rtl.css 1.98 kB
build/block-library/blocks/navigation/editor.css 1.99 kB
build/block-library/blocks/navigation/style-rtl.css 1.85 kB
build/block-library/blocks/navigation/style.css 1.84 kB
build/block-library/blocks/navigation/view.min.js 2.81 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 401 B
build/block-library/blocks/page-list/editor.css 402 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B
build/block-library/blocks/paragraph/editor.css 157 B
build/block-library/blocks/paragraph/style-rtl.css 273 B
build/block-library/blocks/paragraph/style.css 273 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/style-rtl.css 446 B
build/block-library/blocks/post-comments-form/style.css 446 B
build/block-library/blocks/post-comments/style-rtl.css 521 B
build/block-library/blocks/post-comments/style.css 521 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 721 B
build/block-library/blocks/post-featured-image/editor.css 721 B
build/block-library/blocks/post-featured-image/style-rtl.css 153 B
build/block-library/blocks/post-featured-image/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 323 B
build/block-library/blocks/post-template/style.css 323 B
build/block-library/blocks/post-terms/style-rtl.css 73 B
build/block-library/blocks/post-terms/style.css 73 B
build/block-library/blocks/post-title/style-rtl.css 80 B
build/block-library/blocks/post-title/style.css 80 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 198 B
build/block-library/blocks/pullquote/editor.css 198 B
build/block-library/blocks/pullquote/style-rtl.css 389 B
build/block-library/blocks/pullquote/style.css 388 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 234 B
build/block-library/blocks/query-pagination/style.css 231 B
build/block-library/blocks/query/editor-rtl.css 131 B
build/block-library/blocks/query/editor.css 132 B
build/block-library/blocks/quote/style-rtl.css 201 B
build/block-library/blocks/quote/style.css 201 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 132 B
build/block-library/blocks/read-more/style.css 132 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 397 B
build/block-library/blocks/search/style.css 398 B
build/block-library/blocks/search/theme-rtl.css 64 B
build/block-library/blocks/search/theme.css 64 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 245 B
build/block-library/blocks/separator/style.css 245 B
build/block-library/blocks/separator/theme-rtl.css 172 B
build/block-library/blocks/separator/theme.css 172 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 744 B
build/block-library/blocks/site-logo/editor.css 744 B
build/block-library/blocks/site-logo/style-rtl.css 181 B
build/block-library/blocks/site-logo/style.css 181 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 84 B
build/block-library/blocks/site-title/editor.css 84 B
build/block-library/blocks/social-link/editor-rtl.css 177 B
build/block-library/blocks/social-link/editor.css 177 B
build/block-library/blocks/social-links/editor-rtl.css 674 B
build/block-library/blocks/social-links/editor.css 673 B
build/block-library/blocks/social-links/style-rtl.css 1.37 kB
build/block-library/blocks/social-links/style.css 1.36 kB
build/block-library/blocks/spacer/editor-rtl.css 332 B
build/block-library/blocks/spacer/editor.css 332 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 471 B
build/block-library/blocks/table/editor.css 472 B
build/block-library/blocks/table/style-rtl.css 481 B
build/block-library/blocks/table/style.css 481 B
build/block-library/blocks/table/theme-rtl.css 188 B
build/block-library/blocks/table/theme.css 188 B
build/block-library/blocks/tag-cloud/style-rtl.css 214 B
build/block-library/blocks/tag-cloud/style.css 215 B
build/block-library/blocks/template-part/editor-rtl.css 560 B
build/block-library/blocks/template-part/editor.css 559 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 87 B
build/block-library/blocks/verse/style.css 87 B
build/block-library/blocks/video/editor-rtl.css 571 B
build/block-library/blocks/video/editor.css 572 B
build/block-library/blocks/video/style-rtl.css 173 B
build/block-library/blocks/video/style.css 173 B
build/block-library/blocks/video/theme-rtl.css 124 B
build/block-library/blocks/video/theme.css 124 B
build/block-library/common-rtl.css 921 B
build/block-library/common.css 919 B
build/block-library/editor-rtl.css 10.1 kB
build/block-library/editor.css 10.1 kB
build/block-library/index.min.js 167 kB
build/block-library/reset-rtl.css 474 B
build/block-library/reset.css 474 B
build/block-library/style-rtl.css 11.1 kB
build/block-library/style.css 11.1 kB
build/block-library/theme-rtl.css 672 B
build/block-library/theme.css 676 B
build/block-serialization-default-parser/index.min.js 1.09 kB
build/block-serialization-spec-parser/index.min.js 2.79 kB
build/blocks/index.min.js 46.4 kB
build/components/style-rtl.css 15.5 kB
build/components/style.css 15.5 kB
build/compose/index.min.js 11.2 kB
build/core-data/index.min.js 13.4 kB
build/customize-widgets/index.min.js 11.4 kB
build/customize-widgets/style-rtl.css 1.5 kB
build/customize-widgets/style.css 1.49 kB
build/data-controls/index.min.js 631 B
build/date/index.min.js 31.9 kB
build/deprecated/index.min.js 485 B
build/dom-ready/index.min.js 304 B
build/dom/index.min.js 4.5 kB
build/edit-navigation/index.min.js 16.2 kB
build/edit-navigation/style-rtl.css 3.76 kB
build/edit-navigation/style.css 3.76 kB
build/edit-post/classic-rtl.css 546 B
build/edit-post/classic.css 547 B
build/edit-site/style-rtl.css 7.22 kB
build/edit-site/style.css 7.21 kB
build/edit-widgets/index.min.js 16.7 kB
build/edit-widgets/style-rtl.css 4.17 kB
build/edit-widgets/style.css 4.17 kB
build/editor/style-rtl.css 3.71 kB
build/editor/style.css 3.71 kB
build/element/index.min.js 3.29 kB
build/escape-html/index.min.js 517 B
build/format-library/index.min.js 6.58 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.63 kB
build/html-entities/index.min.js 424 B
build/i18n/index.min.js 3.75 kB
build/is-shallow-equal/index.min.js 501 B
build/keyboard-shortcuts/index.min.js 1.8 kB
build/keycodes/index.min.js 1.39 kB
build/list-reusable-blocks/index.min.js 1.72 kB
build/list-reusable-blocks/style-rtl.css 838 B
build/list-reusable-blocks/style.css 838 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 925 B
build/nux/index.min.js 2.09 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/plugins/index.min.js 1.95 kB
build/primitives/index.min.js 917 B
build/priority-queue/index.min.js 582 B
build/react-i18n/index.min.js 671 B
build/react-refresh-entry/index.min.js 8.44 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.65 kB
build/reusable-blocks/index.min.js 2.22 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 11 kB
build/server-side-render/index.min.js 1.58 kB
build/shortcode/index.min.js 1.49 kB
build/token-list/index.min.js 639 B
build/url/index.min.js 1.9 kB
build/viewport/index.min.js 1.05 kB
build/warning/index.min.js 248 B
build/widgets/index.min.js 7.15 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.04 kB

compressed-size-action

@adamziel adamziel added the Developer Experience Ideas about improving block and theme developer experience label Jan 21, 2022
Copy link
Member

@jsnajdr jsnajdr left a comment

Choose a reason for hiding this comment

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

One thing I dislike a bit is that registry.resolveSelect( store ).selector() and useResolveSelect( ( resolve ) => resolve( store ).selector() ) return very different things. One returns a promise, the other a { data, ...progressMetadata } structure.

It's unlike useSelect which is perfectly symmetrical with registry.select.

Maybe a different name, like useQuerySelect, is better? In the sense I described above, useResolveSelect is a bit misleading name 🙂

Other than that, this new hook is nicely compatible with the potential useSuspenseSelect hook. They all differ only in what the bound selectors return:

  • useSelect selectors return synchronously the current data in store
  • useResolveSelect selectors return the sync data plus progress metadata in one package
  • useSuspenseSelect selectors return the resolved data and throw if the selector is unresolved

packages/data/src/components/use-resolve-select/index.js Outdated Show resolved Hide resolved
packages/data/src/components/use-resolve-select/index.js Outdated Show resolved Hide resolved
@adamziel adamziel mentioned this pull request Jan 21, 2022
@kevin940726
Copy link
Member

Some first thoughts:

useResolveSelect still seems to be a bit bloated for simple usage. I believe the intended use case for this new hook is to introduce an easier-to-use API for day-to-day data fetching scenarios. useSelect isn't going to be deprecated, so we should probably make these two as different as possible so that there's no confusion for choosing the better-suited API.

So instead of this:

const { data, isResolving, hasFinished } = useResolveSelect(
	( resolve ) => resolve( coreStore ).getEntityRecord( 'postType', 'page', pageId ),
	[ pageId ]
};

We should probably just do this (as stated in the PR description):

const { data, isResolving, hasFinished } = useEntityRecord( 'postType', 'page', pageId );

Resolving data is almost only useful for entity records, I don't see the use cases for using it for stores other than coreStore. For the same reason, we don't need to provide getEntityRecord either, we can switch between getEntityRecord and getEntityRecords under the hood automatically depending on the arguments. Or, we can introduce a different hook useEntityRecords for explicitness.

What about getMenus, getWidgets, or any other entity-specific helpers? I'd suggest just using react hooks to create those. For instance:

const useWidgets = () => useEntityRecords( 'root', 'widget', { per_page: -1, _embed: 'about' } );

You can then perform other memoization or transformations using useMemo later.

This is just my opinion though, and I'm still very happy with what this PR proposes (thank you again!). WDYT?

@adamziel
Copy link
Contributor Author

adamziel commented Jan 25, 2022

Maybe a different name, like useQuerySelect, is better? In the sense I described above, useResolveSelect is a bit misleading name 🙂

I agree, useQuerySelect sounds like a better match 👍

useResolveSelect still seems to be a bit bloated for simple usage.

I agree. I don't see that many matches for .hasFinishedResolution either, and those that are there refer to getEntityRecord and getEntityRecords (plus some aliases of these two like getMenus, and getWidgetType).

I like the API you suggested, with one exception: I'd rather have two separate functions than one reasoning about the argument data type.

I wonder if it would make sense to implement that on top of useQuerySelect? The simplicity of useEntityRecord is nice, but I fear some synchronization issues when there are multiple usages of that hook. With a single useQuerySelect call, things are reasonably atomic. I'll explore a bit further and update this PR. Let's see where the discussion takes us from there.

@jsnajdr
Copy link
Member

jsnajdr commented Jan 27, 2022

I wonder if it would make sense to implement that on top of useQuerySelect?

I think that useQuerySelect is a useful primitive on top of which other hooks like useEntityRecord can be built. @wordpress/data is quite widely used outside Gutenberg. In many Automattic project, in WooCommerce and many other plugins. It's not correct to assume that coreStore is the only store that has selectors with resolvers -- any store that fetches stuff from a REST API will have these.

@adamziel adamziel mentioned this pull request Jan 27, 2022
@adamziel adamziel changed the title Propose useResolveSelect Propose useQuerySelect Jan 27, 2022
@adamziel
Copy link
Contributor Author

adamziel commented Jan 27, 2022

I think that useQuerySelect is a useful primitive on top of which other hooks like useEntityRecord can be built.

Great synchrony! I was working on just that. This PR now implements useQuerySelect as a low-level backbone, and #38135 adds a high-level useEntityRecord* API on top of it.

Since there comments here are favorable, I will start cleaning things up and add unit tests, documentation, and perhaps some type definitions.

@adamziel
Copy link
Contributor Author

adamziel commented Feb 1, 2022

I'll skip the detailed type definitions for now and go with similar ones as useSelect. More rigid typing would have to start with useSelect and is out of scope for this PR. Here's what I played with in case i could come handy one day:

type Select<Config> = (StoreDescriptor<Config>) => SelectorsOf<StoreDescriptor<Config>>
type MapSelect<T> = (Select, DataRegistry) => T
type useSelect<T> = (MapSelect<T>, any[]) => T

type QuerySelectorsOf< Config extends AnyConfig > = Config extends ReduxStoreConfig<
	any,
	any,
	infer Selectors
>
	? { [ name in keyof Selectors ]: Function }
	: never;

type QuerySelect<Config> = (StoreDescriptor<Config>) => QuerySelectorsOf<StoreDescriptor<Config>>
type MapQuerySelect<T> = (QuerySelect, DataRegistry) => T
type useQuerySelect<T> = (MapQuerySelect<T>, any[]) => T

@adamziel adamziel force-pushed the propose/use-resolve-select branch from 26ee0b2 to 72e64e2 Compare February 1, 2022 15:30
@adamziel
Copy link
Contributor Author

adamziel commented Feb 1, 2022

This one is ready for a review! CC @jsnajdr @kevin940726

@adamziel adamziel changed the title Propose useQuerySelect Add useQuerySelect to core/data Feb 4, 2022
@adamziel adamziel force-pushed the propose/use-resolve-select branch from 447b5f2 to 5ae67d7 Compare February 7, 2022 14:13
@adamziel adamziel force-pushed the propose/use-resolve-select branch from 4e162c0 to 4b51ec9 Compare February 8, 2022 11:05
Copy link
Member

@kevin940726 kevin940726 left a comment

Choose a reason for hiding this comment

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

TBH, I still don't think we need this hook. IMO, we only need a high level API (useEntityRecord) and a low level API (useSelect) but this hook is something in between. Sure, it can help us create hooks for stores other than core-data, but given that these hooks are still experimental, I'm not sure if it's really helpful to create multiple hierarchies now.

I'm not against it though it people really think this is helpful now and are willing to expect that things could change 😅 .


interface QuerySelectResponse {
/** the requested selector return value */
data: Object;
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to change this to generic too?

data: Object;

/** is the record still being resolved? Via the `getIsResolving` meta-selector */
isResolving: boolean;
Copy link
Member

Choose a reason for hiding this comment

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

Since this is a lower-level API, I'd imagine it to include status as well.

hasStarted: boolean;

/** has the resolution finished? Via the `hasFinishedResolution` meta-selector. */
hasResolved: boolean;
Copy link
Member

Choose a reason for hiding this comment

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

As mentioned in #38522 (comment), I'm not sure what's the difference between this and !!data? We can discuss this in the original thread though.

return useSelect( ( select, registry ) => {
const resolve = ( store ) => enrichSelectors( select( store ) );
return mapQuerySelect( resolve, registry );
}, deps );
Copy link
Member

Choose a reason for hiding this comment

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

IMO, adding deps to a custom hook is a bad idea. I wonder if we could somehow discourage it in this API by always returning the latest mapQuerySelect possible? Something like:

const latestMapQuerySelectRef = useLatestRef( mapQuerySelect );

return useSelect( ( select, registry ) => {
	const resolve = ( store ) => enrichSelectors( select( store ) );
	return latestMapQuerySelectRef.current( resolve, registry );
}, [] );

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wait, that's just a proxy to useSelect, what's wrong with having deps?

if ( META_SELECTORS.includes( selectorName ) ) {
continue;
}
Object.defineProperty( resolvers, selectorName, {
Copy link
Member

Choose a reason for hiding this comment

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

Any reason why we're using Object.defineProperty rather than simply mutating the object? I guess TS won't generate the correct typings for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's because we're defining lazy getters that won't be evaluated until they're called for the first time.

Comment on lines +1 to +15
export const META_SELECTORS = [
'getIsResolving',
'hasStartedResolution',
'hasFinishedResolution',
'isResolving',
'getCachedResolvers',
];

const META_ACTIONS = [
'startResolution',
'finishResolution',
'invalidateResolution',
'invalidateResolutionForStore',
'invalidateResolutionForStoreSelector',
];
Copy link
Member

Choose a reason for hiding this comment

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

Nit: We can move these to constants.ts and type them as const to get the desired typings.

export const META_SELECTORS = [
	'getIsResolving',
	'hasStartedResolution',
	'hasFinishedResolution',
	'isResolving',
	'getCachedResolvers',
] as const;

@gziolo
Copy link
Member

gziolo commented Feb 24, 2022

Should we close this PR now that userQuerySelect is an internal hook in @wordpress/core-data package?

https://github.com/WordPress/gutenberg/blob/trunk/packages/core-data/src/hooks/use-query-select.ts

The alternative I saw discussed in other places would be to rebase this PR and move the hook to @wordpress/data.

@adamziel
Copy link
Contributor Author

adamziel commented Apr 1, 2022

Yes, let's close this PR @gziolo. I believe useQuerySelect would still be a useful public API, but we can discuss it separately in another PR.

@adamziel adamziel closed this Apr 1, 2022
@gziolo gziolo deleted the propose/use-resolve-select branch April 1, 2022 10:56
@gziolo
Copy link
Member

gziolo commented Apr 1, 2022

The code is there so it should be mostly moving code from one place to another 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Developer Experience Ideas about improving block and theme developer experience [Package] Data /packages/data
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants