Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Experiments: sharing private APIs with lock() and unlock() (#46131)
## Description This commit introduces a more convenient API for managing the private experiments as the former `register`-based API was quite cumbersome to use. The idea is to "lock" private data inside public objects: ```js const { lock, unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules( '<CONSENT STRING>', '@wordpress/blocks' ); export const publicObject = {}; lock( __experiments, "Shh, private data!" ); publicObject // {} unlock( publicObject ) // "Shh, private data!" ``` This new `lock()`/`unlock()` API enables private functions, classes, components, selectors, actions, arguments, and properties. Any package that opted-in to private APIs can call `unlock()` on publicly available artifacts to retrieve the related private API. Kudos to @jsnajdr for [identifying an opportunity to simplify the API](#44521 (comment))! ## Examples ### Private selectors: ```js // In wordpress/block-data: import { store as blockEditorStore } from './store'; import { unlock } from '../experiments'; import { __unstableSelectionHasUnmergeableBlock } from './store/selectors'; unlock( store ).registerPrivateSelectors( { __unstableSelectionHasUnmergeableBlock } ); // In a React component: function MyComponent() { const hasRole = useSelect( ( select ) => ( unlock( select( blockEditorStore ) ).__unstableSelectionHasUnmergeableBlock() ) ); // ... } ``` ### Private functions, classes, and variables ```js // In packages/package1/index.js: import { lock } from './experiments'; export const experiments = {}; /* Attach private data to the exported object */ lock(experiments, { __experimentalCallback: function() {}, __experimentalReactComponent: function ExperimentalComponent() { return <div/>; }, __experimentalClass: class Experiment{}, __experimentalVariable: 5, }); // In packages/package2/index.js: import { experiments } from '@wordpress/package1'; import { unlock } from './experiments'; const { __experimentalCallback, __experimentalReactComponent, __experimentalClass, __experimentalVariable } = unlock( experiments ); ``` ### Private function arguments To add an experimental argument to a stable function you'll need to prepare a stable and an experimental version of that function. Then, export the stable function and `lock()` the unstable function inside it: ```js // In @wordpress/package1/index.js: import { lock } from './experiments'; // The experimental function contains all the logic function __experimentalValidateBlocks(formula, __experimentalIsStrict) { let isValid = false; // ...complex logic we don't want to duplicate... if ( __experimentalIsStrict ) { // ... } // ...complex logic we don't want to duplicate... return isValid; } // The stable public function is a thin wrapper that calls the // experimental function with the experimental features disabled export function validateBlocks(blocks) { __experimentalValidateBlocks(blocks, false); } lock( validateBlocks, __experimentalValidateBlocks ); // In @wordpress/package2/index.js: import { validateBlocks } from '@wordpress/package1'; import { unlock } from './experiments'; // The experimental function may be "unlocked" given the stable function: const __experimentalValidateBlocks = unlock(validateBlocks); __experimentalValidateBlocks(blocks, true); ``` ### Private React Component properties To add an experimental argument to a stable component you'll need to prepare a stable and an experimental version of that component. Then, export the stable function and `lock()` the unstable function inside it: ```js // In @wordpress/package1/index.js: import { lock } from './experiments'; // The experimental component contains all the logic const ExperimentalMyButton = ( { title, __experimentalShowIcon = true } ) => { // ...complex logic we don't want to duplicate... return ( <button> { __experimentalShowIcon && <Icon src={some icon} /> } { title } </button> ); } // The stable public component is a thin wrapper that calls the // experimental component with the experimental features disabled export const MyButton = ( { title } ) => <ExperimentalMyExistingButton title={ title } __experimentalShowIcon={ false } /> lock(MyButton, ExperimentalMyButton); // In @wordpress/package2/index.js: import { MyButton } from '@wordpress/package1'; import { unlock } from './experiments'; // The experimental component may be "unlocked" given the stable component: const ExperimentalMyButton = unlock(MyButton); export function MyComponent() { return ( <ExperimentalMyButton data={data} __experimentalShowIcon={ true } /> ) } ``` Co-authored-by: Ramon <[email protected]> Co-authored-by: Robert Anderson <[email protected]>
- Loading branch information