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

Entity records hooks #38135

Closed
wants to merge 22 commits into from
Closed

Entity records hooks #38135

wants to merge 22 commits into from

Conversation

adamziel
Copy link
Contributor

@adamziel adamziel commented Jan 21, 2022

Description

This PR proposes a new hook called useEntityMutation inspired by react-query and @talldan and @kevin940726 feedback. It would used like this:

const { edit, editedRecord, hasEdits, saveEdits, isSaving, saveError } = useEntityMutation( 'postType', 'page', pageId );

I'd like to make core-data more approachable. Building a simple form currently requires a snippet like this:

const {
	editEntityRecord,
	saveEditedEntityRecord
} = useDispatch( coreStore );

const { editedRecord, hasEdits, isSaving, saveError } = useSelect(
	( select ) => {
		const args = [kind, type, id];
		return {
			editedRecord: select( coreStore ).getEditedEntityRecord( ...args ),
			hasEdits: select( coreStore ).hasEditsForEntityRecord( ...args ),
			isSaving: select( coreStore ).isSavingEntityRecord( ...args ),
			saveError: select( coreStore ).getLastEntitySaveError( ...args ),
		};
	},
	[kind, type, id],
);

It is verbose and confusing for new developers. Even knowing about all these functions requires some prior expertise. Having a single entry point to working with entities makes core-data more approachable and turns the challenge from how do I even start? to how to use the available parts?

Together with the upcoming data layer documentation, it could lower the barrier of entry for new contributors, and make the life of existing contributors easier.

This large PR was split into smaller easily mergable PRs:

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

@adamziel adamziel added [Package] Core data /packages/core-data Developer Experience Ideas about improving block and theme developer experience labels Jan 21, 2022
@adamziel adamziel requested a review from nerrad as a code owner January 21, 2022 13:17
@adamziel adamziel self-assigned this Jan 21, 2022
@github-actions
Copy link

github-actions bot commented Jan 21, 2022

Size Change: +868 B (0%)

Total Size: 1.13 MB

Filename Size Change
build/block-library/index.min.js 166 kB +87 B (0%)
build/core-data/index.min.js 14 kB +781 B (+6%) 🔍
ℹ️ 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.21 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/index.min.js 141 kB
build/block-editor/style-rtl.css 14.6 kB
build/block-editor/style.css 14.6 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/cover/editor-rtl.css 546 B
build/block-library/blocks/cover/editor.css 547 B
build/block-library/blocks/cover/style-rtl.css 1.22 kB
build/block-library/blocks/cover/style.css 1.22 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.6 kB
build/block-library/blocks/gallery/style.css 1.6 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 507 B
build/block-library/blocks/image/style.css 511 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 137 B
build/block-library/blocks/latest-posts/editor.css 137 B
build/block-library/blocks/latest-posts/style-rtl.css 528 B
build/block-library/blocks/latest-posts/style.css 527 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.99 kB
build/block-library/blocks/navigation/editor.css 2 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 305 B
build/block-library/blocks/post-template/style.css 305 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 187 B
build/block-library/blocks/quote/style.css 187 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 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 670 B
build/block-library/blocks/social-links/editor.css 669 B
build/block-library/blocks/social-links/style-rtl.css 1.32 kB
build/block-library/blocks/social-links/style.css 1.32 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 908 B
build/block-library/common.css 905 B
build/block-library/editor-rtl.css 10.1 kB
build/block-library/editor.css 10.1 kB
build/block-library/reset-rtl.css 474 B
build/block-library/reset.css 474 B
build/block-library/style-rtl.css 10.8 kB
build/block-library/style.css 10.8 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/index.min.js 214 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/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/data/index.min.js 7.89 kB
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 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-post/index.min.js 29.6 kB
build/edit-post/style-rtl.css 7.15 kB
build/edit-post/style.css 7.14 kB
build/edit-site/index.min.js 37.7 kB
build/edit-site/style-rtl.css 6.85 kB
build/edit-site/style.css 6.84 kB
build/edit-widgets/index.min.js 16.5 kB
build/edit-widgets/style-rtl.css 4.17 kB
build/edit-widgets/style.css 4.17 kB
build/editor/index.min.js 38.4 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.08 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/plugins/index.min.js 1.84 kB
build/primitives/index.min.js 924 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

@spencerfinnell
Copy link

spencerfinnell commented Jan 21, 2022

I have recreated a similar hook in past projects and would love to see a core implementation. I have called mine useEntityRecord.

@adamziel
Copy link
Contributor Author

adamziel commented Jan 21, 2022

I have recreated a similar hook in projects in the past and would love to see a core implementation. I have called mine useEntityRecord.

Thanks for speaking out @spencerfinnell! That hook name rings a bell, too – see this related PR: #38134

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.

While we're at it. I think this PR (and #38134) is a good candidate to start using TypeScript?

packages/core-data/src/use-entity-mutation.js Outdated Show resolved Hide resolved
@gziolo
Copy link
Member

gziolo commented Jan 24, 2022

It's a very interesting proposal, @adamziel. Thank you for opening the PR that explains the idea. It would be helpful to update a few existing components to present how this impacts the developer experience. By the way, have you explored how often these selectors are used together in the Gutenberg project or how likely that plugins would need the new API proposed?

I have recreated a similar hook in past projects and would love to see a core implementation. I have called mine useEntityRecord.

I think naming here makes a big difference. When I see useEntityRecord, it makes me think it offers full CRUD (Create, read, update, and delete) support. Whereas in the API proposed, the "create" part is omitted, so I assume it's because the hook useEntityMutation is about handling edits only. Now, the question is whether we would like to introduce a set of hooks or one hook to rule them all?

@gziolo
Copy link
Member

gziolo commented Jan 24, 2022

By the way, have you considered using Proxy API to make updates to the record simpler to code? Immer or Valtio would be a good example of using immutability for mutable state 😄 Instead of dispatching editEntityRecord (or something similar), users could do the following:

const { editedRecord } = useEntityMutation( 'postType', 'page', pageId );

function onEdit() {
    editedRecord.title = "New title";
    editedRecord.content = "New content.";
}

The only concern here would be that it makes batching updates a bit more complex to handle. Anyway, just an idea since we talk about a completely new hook.

@spencerfinnell
Copy link

spencerfinnell commented Jan 24, 2022

I think naming here makes a big difference. When I see useEntityRecord, it makes me think it offers full CRUD (Create, read, update, and delete) support.

I think useEntityRecord pairs well with the existing useEntityProp which doesn't provide full CRUD support.

By the way, have you explored how often these selectors are used together in the Gutenberg project or how likely that plugins would need the new API proposed?

Paired with #27859, #38134 (comment) developers would have a very good starting place to consume custom entities through custom REST API endpoints, which has been a requirement for the majority of things I've worked on within the block editor or block editor-style admin page.

Copy link
Contributor

@getdave getdave left a comment

Choose a reason for hiding this comment

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

I really really like the motivation to simplify working with the data layer. At the moment it requires too much overhead and this is a good move towards making it a lot more accessible.
👏👏👏

My personal opinion is that we should keep things simple for now and just run with creating these new hooks. We could consider making them experimental just in case we want to build space for tweaking the APIs in response to feedback on the Plugin.

packages/core-data/src/use-entity-mutation.js Outdated Show resolved Hide resolved
packages/core-data/src/use-entity-mutation.js Outdated Show resolved Hide resolved
packages/core-data/src/use-entity-mutation.js Outdated Show resolved Hide resolved
packages/core-data/src/use-entity-mutation.js Outdated Show resolved Hide resolved
);

return {
...mutationFunctions,
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the resulting API I'm wondering whether it might make for more readable components if we instead expose an object on a mutations property.

That would mean we'd call mutations.edit() rather than just edit().

Just an idea.

Copy link
Contributor Author

@adamziel adamziel Jan 27, 2022

Choose a reason for hiding this comment

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

There's much less data coming out of this hook now – do you think that removes the need for the additional nesting level (edit)?

@adamziel adamziel changed the base branch from trunk to propose/use-resolve-select January 27, 2022 15:53
@adamziel adamziel changed the base branch from propose/use-resolve-select to trunk January 27, 2022 16:08
@adamziel
Copy link
Contributor Author

adamziel commented Jan 27, 2022

I tweaked this PR and ended up with the following read hooks:

  • useEntityRecords
  • useEntityRecord
  • usePermissions

...and useQuerySelect from #38134

#38289 showcases how they make the navigation block ~200 lines leaner.

There are also two write hooks:

  • useEntityRecordCreate
  • useEntityRecordUpdate

I didn't find a good place to showcase them, though. We only save things in a handful of places, and most of the time it's in a redux action and not inside of a React component. I still think these two make valuable additions – perhaps I'll write a made-up example to demonstrate the difference.

@adamziel adamziel changed the title Propose useEntityMutation hook Entity records hooks Jan 27, 2022
Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

#38289 showcases how they make the navigation block ~200 lines leaner.

This is super helpful. I appreciate the efforts to put this PR together 🙇🏻

useEntityRecord is used only once, but useEntityRecords 7 times.

It raised the question of whether the existing useEntityProp got popular because it's so convenient or because using getEntityRecord through the data store was hard to use because it's so low-level. Anyway, it's a clear sign that we need those APIs.

I didn't find a good place to showcase them, though. We only save things in a handful of places, and most of the time it's in a redux action and not inside of a React component. I still think these two make valuable additions – perhaps I'll write a made-up example to demonstrate the difference.

Interesting, does it mean we don't need those two hooks at the moment for usage inside Gutenberg? It's a signal that maybe we don't need to rush with that part.

Have you considered whether useEntityRecord could be combined with useEntityRecordCreate and useEntityRecordUpdate? useEntityRecordUpdate handles edit and delete, so there could be also the question whether we need useEntityRecordDelete for consistency. I'm not sure yet what would be better from the user's perspective. One thing is clear if a single component displays data from the record, and you can change that data with a callback, then you would prefer to use now useEntityRecordUpdate which sort of makes useEntityRecord less useful.

import { IDLE, SUCCESS, RESOLVING } from './constants';
import { store as coreStore } from '../';

export default function usePermissions( resource, id ) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: for consistency, it might use a more explicit name like useEntityRecordPermissions since it works only for a single record.

What's the story for multiple records?

Comment on lines +44 to +46
canCreate: create.data,
canUpdate: update?.data,
canDelete: _delete?.data,
Copy link
Member

Choose a reason for hiding this comment

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

Based on the usage in #38289, it might be convenient to assume that permissions return value value only when resolved:

Suggested change
canCreate: create.data,
canUpdate: update?.data,
canDelete: _delete?.data,
canCreate: hasResolved && create.data,
canUpdate: hasResolved && update?.data,
canDelete: hasResolved && _delete?.data,

This way you still have granular control over the status but you don't need to guard values.

Copy link
Member

Choose a reason for hiding this comment

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

I think this makes sense. We should assume that users don't have permission until a query is resolved.

const count = useSelect( ( select ) => {
return select( 'core/block-editor' ).getBlockCount();
}, [] );
const count = useSelect( ( select ) => {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: it looks like you formatted all markdown files using a different tool or Prettier config.

kind,
type,
id,
options = { runIf: true }
Copy link
Member

Choose a reason for hiding this comment

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

It looks like a special case that could be handled outside the hook. Can you explain the rationale behind runIf and how do you envision its application?

@talldan
Copy link
Contributor

talldan commented Feb 1, 2022

  • useEntityRecordCreate
  • useEntityRecordUpdate

Just taking a look at this for the first time. My initial instinct, given the names of these hooks are styled in a CRUD fashion, is that there would also be a useEntityRecordDelete, and was a little surprised to find it part of useEntityRecordUpdate.

I'm wondering if there are a lot of use cases where the code for deleting something will live alongside the code for editing and saving the edited value.

The other bit of feedback is that it'd be great if these hooks could work the same way as useEntityProp where the id is optional and can be provided by an EntityProvider:

export function useEntityProp( kind, type, prop, _id ) {
const providerId = useEntityId( kind, type );
const id = _id ?? providerId;

It's a nice little convenience that helps avoid passing ids or slugs around.

@adamziel
Copy link
Contributor Author

adamziel commented Feb 4, 2022

Thank you for all the reviews! I propose we move forward in a series of atomic PRs focused on one feature at a time. It's been great to explore and diverge in a single place, and now smaller PRs should make it easier to converge on a solution.

The first smaller PR is for the useEntityRecord hook – let's make it happen! :-)

@gziolo gziolo added [Type] Technical Prototype Offers a technical exploration into an idea as an example of what's possible [Type] New API New API to be used by plugin developers or package users. labels Feb 9, 2022
@adamziel
Copy link
Contributor Author

useEntityRecord is shipped! Let's now make the (plural) useEntityRecords happen.

@adamziel
Copy link
Contributor Author

I also created a separate PR for useResourcePermissions.

@gziolo
Copy link
Member

gziolo commented Feb 14, 2022

Thank you for the update @adamziel. Let's make it clear that all those APIs start as experimental so it's __experimentalUseEntityRecord in @wordpress/core-data, and so on.

@gziolo
Copy link
Member

gziolo commented Mar 25, 2022

I see that @adamziel opened #39595 that could use broader feedback:

Which doesn't do much for creating and removing records, so we could also ditch these hooks and settle only for the useEntityRecord hook:

const { editedRecord, applyEdits, saveEdits } = useEntityRecord();

The idea would be to add more functionality to
useEntityRecord.

@gziolo
Copy link
Member

gziolo commented May 7, 2022

Any work left on this PR? We’ve shipped a few proposals already.

@adamziel
Copy link
Contributor Author

adamziel commented May 9, 2022

@gziolo There are two more PRs in motion:

I just refreshed the first one, and the second one still requires a rebase.

@adamziel
Copy link
Contributor Author

adamziel commented Aug 16, 2022

This PR has been fully merged through a series of smaller PRs as explained in the description. Let's close it. Thank you for all the feedback and support!

@adamziel adamziel closed this Aug 16, 2022
@gziolo gziolo deleted the propose/use-entity-mutation branch August 16, 2022 12:14
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] Core data /packages/core-data [Type] New API New API to be used by plugin developers or package users. [Type] Technical Prototype Offers a technical exploration into an idea as an example of what's possible
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants