Add gemeric hook-generator for data access

This commit is contained in:
Philipp Stracker 2025-01-16 18:20:03 +01:00
parent bd14ea441e
commit f07d9bad82
No known key found for this signature in database
2 changed files with 66 additions and 15 deletions

View file

@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n';
import { useCallback, useState } from '@wordpress/element'; // Temporary import { useCallback, useState } from '@wordpress/element'; // Temporary
import { useSelect, useDispatch } from '@wordpress/data'; import { useSelect, useDispatch } from '@wordpress/data';
import { createHooksForStore } from '../utils';
import { STORE_NAME } from './constants'; import { STORE_NAME } from './constants';
import { import {
STYLING_COLORS, STYLING_COLORS,
@ -21,28 +22,17 @@ import {
STYLING_SHAPES, STYLING_SHAPES,
} from './configuration'; } from './configuration';
const useTransient = ( key ) =>
useSelect(
( select ) => select( STORE_NAME ).transientData()?.[ key ],
[ key ]
);
const usePersistent = ( key ) =>
useSelect(
( select ) => select( STORE_NAME ).persistentData()?.[ key ],
[ key ]
);
const useHooks = () => { const useHooks = () => {
const { persist, setShape } = useDispatch( STORE_NAME ); const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
const { persist } = useDispatch( STORE_NAME );
// Read-only flags and derived state. // Read-only flags and derived state.
// Transient accessors. // Transient accessors.
const isReady = useTransient( 'isReady' ); const [ isReady ] = useTransient( 'isReady' );
// Persistent accessors. // Persistent accessors.
const shape = usePersistent( 'shape' ); const [ shape, setShape ] = usePersistent( 'shape' );
return { return {
persist, persist,

View file

@ -1,3 +1,6 @@
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
/** /**
* Updates an object with new values, filtering based on allowed keys. * Updates an object with new values, filtering based on allowed keys.
* *
@ -13,6 +16,10 @@ const updateObject = ( oldObject, newValues, allowedKeys = {} ) => ( {
...Object.keys( newValues ).reduce( ( acc, key ) => { ...Object.keys( newValues ).reduce( ( acc, key ) => {
if ( key in allowedKeys ) { if ( key in allowedKeys ) {
acc[ key ] = newValues[ key ]; acc[ key ] = newValues[ key ];
} else {
console.warn(
`Ignoring unknown key "${ key }" - to use it, add it to the initial store properties in the reducer.`
);
} }
return acc; return acc;
}, {} ), }, {} ),
@ -73,3 +80,57 @@ export const createReducer = (
return state; return state;
}; };
}; };
/**
* Returns an object with two hooks:
* - useTransient( prop )
* - usePersistent( prop )
*
* Both hooks have a similar syntax to the native "useState( prop )" hook, but provide access to
* a transient or persistent property in the relevant Redux store.
*
* Sample:
*
* const { useTransient } = createHooksForStore( STORE_NAME );
* const [ isReady, setIsReady ] = useTransient( 'isReady' );
*
* @param {string} storeName Store name.
* @return {{useTransient, usePersistent}} Store hooks.
*/
export const createHooksForStore = ( storeName ) => {
const createHook = ( selector, dispatcher ) => ( key ) => {
const value = useSelect(
( select ) => {
const store = select( storeName );
if ( ! store?.[ selector ] ) {
throw new Error(
`Please create the selector "${ selector }" for store "${ storeName }"`
);
}
return store[ selector ]()?.[ key ];
},
[ key ]
);
const actions = useDispatch( storeName );
const setValue = useCallback(
( newValue ) => {
if ( ! actions?.[ dispatcher ] ) {
throw new Error(
`Please create the action "${ dispatcher }" for store "${ storeName }"`
);
}
actions[ dispatcher ]( key, newValue );
},
[ actions, key ]
);
return [ value, setValue ];
};
return {
useTransient: createHook( 'transientData', 'setTransient' ),
usePersistent: createHook( 'persistentData', 'setPersistent' ),
};
};