Skip to content

Commit

Permalink
Add mutations data and helper functions to useEntityRecord (#39595)
Browse files Browse the repository at this point in the history
Add mutations data and helper functions to __experimentalUseEntityRecord:

```js
const { record, edit, editedRecord, hasEdits, save } = useEntityRecord( 'postType', 'page', pageId );
// Or just:
const page = useEntityRecord( 'postType', 'page', pageId );
page.edit({ title: newTitle });
page.save();
```

Co-authored-by: Greg Ziółkowski <grzegorz@gziolo.pl>
  • Loading branch information
adamziel and gziolo committed Aug 8, 2022
1 parent e2b293d commit 54c9d81
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 3 deletions.
51 changes: 51 additions & 0 deletions packages/core-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,57 @@ In the above example, when `PageTitleDisplay` is rendered into an
application, the page and the resolution details will be retrieved from
the store state using `getEntityRecord()`, or resolved if missing.

```js
import { useState } from '@wordpress/data';
import { useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { TextControl } from '@wordpress/components';
import { store as noticeStore } from '@wordpress/notices';
import { useEntityRecord } from '@wordpress/core-data';

function PageRenameForm( { id } ) {
const page = useEntityRecord( 'postType', 'page', id );
const [ title, setTitle ] = useState( () => page.record.title.rendered );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticeStore );

if ( page.isResolving ) {
return 'Loading...';
}

async function onRename( event ) {
event.preventDefault();
page.edit( { title } );
try {
await page.save();
createSuccessNotice( __( 'Page renamed.' ), {
type: 'snackbar',
} );
} catch ( error ) {
createErrorNotice( error.message, { type: 'snackbar' } );
}
}

return (
<form onSubmit={ onRename }>
<TextControl
label={ __( 'Name' ) }
value={ title }
onChange={ setTitle }
/>
<button type="submit">{ __( 'Save' ) }</button>
</form>
);
}

// Rendered in the application:
// <PageRenameForm id={ 1 } />
```

In the above example, updating and saving the page title is handled
via the `edit()` and `save()` mutation helpers provided by
`useEntityRecord()`;

_Parameters_

- _kind_ `string`: Kind of the entity, e.g. `root` or a `postType`. See rootEntitiesConfig in ../entities.ts for a list of available kinds.
Expand Down
10 changes: 9 additions & 1 deletion packages/core-data/src/hooks/test/use-entity-record.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ describe( 'useEntityRecord', () => {
);

expect( data ).toEqual( {
records: undefined,
edit: expect.any( Function ),
editedRecord: {},
hasEdits: false,
record: undefined,
save: expect.any( Function ),
hasResolved: false,
isResolving: false,
status: 'IDLE',
Expand All @@ -66,7 +70,11 @@ describe( 'useEntityRecord', () => {
} );

expect( data ).toEqual( {
edit: expect.any( Function ),
editedRecord: {},
hasEdits: false,
record: { hello: 'world', id: 1 },
save: expect.any( Function ),
hasResolved: true,
isResolving: false,
status: 'SUCCESS',
Expand Down
99 changes: 97 additions & 2 deletions packages/core-data/src/hooks/use-entity-record.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/**
* WordPress dependencies
*/
import { useDispatch, useSelect } from '@wordpress/data';
import deprecated from '@wordpress/deprecated';
import { useMemo } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -14,11 +16,25 @@ export interface EntityRecordResolution< RecordType > {
/** The requested entity record */
record: RecordType | null;

/** The edited entity record */
editedRecord: Partial< RecordType >;

/** Apply local (in-browser) edits to the edited entity record */
edit: ( diff: Partial< RecordType > ) => void;

/** Persist the edits to the server */
save: () => Promise< void >;

/**
* Is the record still being resolved?
*/
isResolving: boolean;

/**
* Does the record have any local edits?
*/
hasEdits: boolean;

/**
* Is the record resolved by now?
*/
Expand Down Expand Up @@ -66,6 +82,58 @@ export interface Options {
* application, the page and the resolution details will be retrieved from
* the store state using `getEntityRecord()`, or resolved if missing.
*
* @example
* ```js
* import { useState } from '@wordpress/data';
* import { useDispatch } from '@wordpress/data';
* import { __ } from '@wordpress/i18n';
* import { TextControl } from '@wordpress/components';
* import { store as noticeStore } from '@wordpress/notices';
* import { useEntityRecord } from '@wordpress/core-data';
*
* function PageRenameForm( { id } ) {
* const page = useEntityRecord( 'postType', 'page', id );
* const [ title, setTitle ] = useState( () => page.record.title.rendered );
* const { createSuccessNotice, createErrorNotice } =
* useDispatch( noticeStore );
*
* if ( page.isResolving ) {
* return 'Loading...';
* }
*
* async function onRename( event ) {
* event.preventDefault();
* page.edit( { title } );
* try {
* await page.save();
* createSuccessNotice( __( 'Page renamed.' ), {
* type: 'snackbar',
* } );
* } catch ( error ) {
* createErrorNotice( error.message, { type: 'snackbar' } );
* }
* }
*
* return (
* <form onSubmit={ onRename }>
* <TextControl
* label={ __( 'Name' ) }
* value={ title }
* onChange={ setTitle }
* />
* <button type="submit">{ __( 'Save' ) }</button>
* </form>
* );
* }
*
* // Rendered in the application:
* // <PageRenameForm id={ 1 } />
* ```
*
* In the above example, updating and saving the page title is handled
* via the `edit()` and `save()` mutation helpers provided by
* `useEntityRecord()`;
*
* @return Entity record data.
* @template RecordType
*/
Expand All @@ -75,7 +143,31 @@ export default function useEntityRecord< RecordType >(
recordId: string | number,
options: Options = { enabled: true }
): EntityRecordResolution< RecordType > {
const { data: record, ...rest } = useQuerySelect(
const { editEntityRecord, saveEditedEntityRecord } =
useDispatch( coreStore );

const mutations = useMemo(
() => ( {
edit: ( record ) =>
editEntityRecord( kind, name, recordId, record ),
save: ( saveOptions: any = {} ) =>
saveEditedEntityRecord( kind, name, recordId, {
throwOnError: true,
...saveOptions,
} ),
} ),
[ recordId ]
);

const { editedRecord, hasEdits } = useSelect(
( select ) => ( {
editedRecord: select( coreStore ).getEditedEntityRecord(),
hasEdits: select( coreStore ).hasEditsForEntityRecord(),
} ),
[ kind, name, recordId ]
);

const { data: record, ...querySelectRest } = useQuerySelect(
( query ) => {
if ( ! options.enabled ) {
return null;
Expand All @@ -87,7 +179,10 @@ export default function useEntityRecord< RecordType >(

return {
record,
...rest,
editedRecord,
hasEdits,
...querySelectRest,
...mutations,
};
}

Expand Down

0 comments on commit 54c9d81

Please sign in to comment.