WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.48k stars 4.18k forks source link

`useEntityRecord` isn't refreshed when db updated externally #47746

Closed iandunn closed 1 month ago

iandunn commented 1 year ago

What problem does this address?

useEntityRecord only knows about updates to the database that it performs itself. If the db is updated by an custom REST API or AJAX endpoint, a cron job, etc, the userRecord doesn't automatically refresh. It only refreshes when userRecord.save() is called.

For example, the app below extends a 3rd party plugin. The 3rd party plugin uses custom REST API endpoints to update usermeta, and some of those values are added to the /users endpoint for convenience.

https://github.com/WordPress/wporg-two-factor/blob/2640734b9295ce7f3891e4d1752ce4dd6d434469/settings/src/components/totp.js#L55-L71

https://github.com/WordPress/wporg-two-factor/blob/33b02cfa68492a0e658302f04dc8db58e1536954/settings/src/components/backup-codes.js#L40-L68

What is your proposed solution?

Add a userRecord.refresh() method that would fetch the latest copy of the resource and update userRecord.record. This workaround demonstrates how I've done that without Core support, but a Core method could be cleaner.

https://github.com/WordPress/wporg-two-factor/blob/00a7507/settings/src/utilities.js#L18-L34

cc @adamziel

Mamaduka commented 1 year ago

We can use the invalidateResolution action for this.

Tropicalista commented 11 months ago

We can use the invalidateResolution action for this.

This will not work if using useEntityRecords.

const stores = useEntityRecords( 'tropical-juice/v1', 'stores' );
const { invalidateResolution } = useDispatch( coreStore );

const invalidateResolver = () => {
    invalidateResolution( 'getEntityRecords', [
        'tropical-juice/v1',
        'stores',
    ] );
};
Mamaduka commented 11 months ago

@Tropicalista, can you share an example code for how you're fetching data with getEntityRecords?

Tropicalista commented 11 months ago

I try to explain better. This code works.

const data = useSelect((select) => {
    return select('core').getEntityRecords( 'tropical-juice/v1', 'stores' );
});

const { invalidateResolution } = useDispatch('core/data');

const invalidateResolver = () => {
    invalidateResolution('core', 'getEntityRecords', ['tropical-juice/v1', 'stores']);
};

Using useEntityRecords does not work,

const stores = useEntityRecords( 'tropical-juice/v1', 'stores' );
const { invalidateResolution } = useDispatch( coreStore );

const invalidateResolver = () => {
    invalidateResolution( 'getEntityRecords', [
        'tropical-juice/v1',
        'stores',
    ] );
};

From my understanding both snippets do exact same thing. However I cannot invalidate cache if I'm using hook.

swissspidy commented 1 month ago

If anyone else is stumbling upon this:

I think the difference between the two is that useEntityRecords() does a getEntityRecords( 'tropical-juice/v1', 'stores',{} ) under the hood (note the third param). So you'd have to adjust your invalidation call accordingly.