diff --git a/package-lock.json b/package-lock.json index 828769e19e435e..f73c82be4d8979 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17539,6 +17539,7 @@ "@wordpress/element": "file:packages/element", "@wordpress/hooks": "file:packages/hooks", "@wordpress/icons": "file:packages/icons", + "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "memize": "^1.1.0" } }, diff --git a/packages/plugins/README.md b/packages/plugins/README.md index 8b7fbaf5d922d3..a5fee590d4fd18 100644 --- a/packages/plugins/README.md +++ b/packages/plugins/README.md @@ -68,6 +68,12 @@ const Layout = () => ( ); ``` +_Parameters_ + +- _props_ `Object`: +- _props.scope_ `string|undefined`: +- _props.onError_ `Function|undefined`: + _Returns_ - `WPComponent`: The component to be rendered. diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 881e6fdf4530a0..8eb18d8512da37 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -30,6 +30,7 @@ "@wordpress/element": "file:../element", "@wordpress/hooks": "file:../hooks", "@wordpress/icons": "file:../icons", + "@wordpress/is-shallow-equal": "file:../is-shallow-equal", "memize": "^1.1.0" }, "peerDependencies": { diff --git a/packages/plugins/src/components/plugin-area/index.js b/packages/plugins/src/components/plugin-area/index.js index 30c9d59371206e..72147dba16292d 100644 --- a/packages/plugins/src/components/plugin-area/index.js +++ b/packages/plugins/src/components/plugin-area/index.js @@ -6,8 +6,9 @@ import memoize from 'memize'; /** * WordPress dependencies */ -import { Component } from '@wordpress/element'; +import { useMemo, useSyncExternalStore } from '@wordpress/element'; import { addAction, removeAction } from '@wordpress/hooks'; +import isShallowEqual from '@wordpress/is-shallow-equal'; /** * Internal dependencies @@ -16,9 +17,14 @@ import { PluginContextProvider } from '../plugin-context'; import { PluginErrorBoundary } from '../plugin-error-boundary'; import { getPlugins } from '../../api'; +const getPluginContext = memoize( ( icon, name ) => ( { icon, name } ) ); + /** * A component that renders all plugin fills in a hidden div. * + * @param {Object} props + * @param {string|undefined} props.scope + * @param {Function|undefined} props.onError * @example * ```js * // Using ES5 syntax @@ -50,80 +56,61 @@ import { getPlugins } from '../../api'; * * @return {WPComponent} The component to be rendered. */ -class PluginArea extends Component { - constructor() { - super( ...arguments ); - - this.setPlugins = this.setPlugins.bind( this ); - this.memoizedContext = memoize( ( name, icon ) => { - return { - name, - icon, - }; - } ); - this.state = this.getCurrentPluginsState(); - } +function PluginArea( { scope, onError } ) { + const store = useMemo( () => { + let lastValue; - getCurrentPluginsState() { return { - plugins: getPlugins( this.props.scope ).map( - ( { icon, name, render } ) => { - return { - Plugin: render, - context: this.memoizedContext( name, icon ), - }; - } - ), - }; - } + subscribe( listener ) { + addAction( + 'plugins.pluginRegistered', + 'core/plugins/plugin-area/plugins-registered', + listener + ); + addAction( + 'plugins.pluginUnregistered', + 'core/plugins/plugin-area/plugins-unregistered', + listener + ); + return () => { + removeAction( + 'plugins.pluginRegistered', + 'core/plugins/plugin-area/plugins-registered' + ); + removeAction( + 'plugins.pluginUnregistered', + 'core/plugins/plugin-area/plugins-unregistered' + ); + }; + }, + getValue() { + const nextValue = getPlugins( scope ); - componentDidMount() { - addAction( - 'plugins.pluginRegistered', - 'core/plugins/plugin-area/plugins-registered', - this.setPlugins - ); - addAction( - 'plugins.pluginUnregistered', - 'core/plugins/plugin-area/plugins-unregistered', - this.setPlugins - ); - } + if ( ! isShallowEqual( lastValue, nextValue ) ) { + lastValue = nextValue; + } - componentWillUnmount() { - removeAction( - 'plugins.pluginRegistered', - 'core/plugins/plugin-area/plugins-registered' - ); - removeAction( - 'plugins.pluginUnregistered', - 'core/plugins/plugin-area/plugins-unregistered' - ); - } + return lastValue; + }, + }; + }, [ scope ] ); - setPlugins() { - this.setState( this.getCurrentPluginsState ); - } + const plugins = useSyncExternalStore( store.subscribe, store.getValue ); - render() { - return ( -