From 1e4363ac1169bdd5d094885168763f95b2352302 Mon Sep 17 00:00:00 2001 From: inwerpsel Date: Fri, 25 Mar 2022 12:51:38 +0100 Subject: [PATCH 1/2] Add useSelectors hook for getting a store's selectors The `useSelect` function is used to cover 2 use cases, switching between them with the first parameter's type. The original use case is to fetch data during render. The 2nd use case simply returns a store's selectors, so they can be called in event handlers. This 2nd use case does not need most of the hooks the original use is calling. However because of how hooks work, they needed to be called anyway in this path. In the new function, the only remaining hook call for getting the store's selectors is to `useRegistry`. It also allows to simplify useSelect, which doesn't need any of the checks on the type of argument anymore, and can simply return the map output. I didn't test the impact of not having to call these hooks, but I guess it's not 0. Especially if many components use this. For now `useSelect` preserves its double function, because it is exported as a public API. In a next step it's possibel to internally already use a simpler form that does 1 thing, and keep a copy of the old one only for exporting the API before it's removed. --- .../src/components/block-actions/index.js | 6 ++-- .../use-block-props/use-focus-handler.js | 4 +-- .../use-block-props/use-multi-selection.js | 0 .../use-block-props/use-nav-mode-exit.js | 6 ++-- .../use-selected-block-event-handlers.js | 4 +-- .../block-list/use-in-between-inserter.js | 4 +-- .../block-selection-clearer/index.js | 4 +-- .../block-tools/block-selection-button.js | 4 +-- .../src/components/block-tools/index.js | 4 +-- .../src/components/copy-handler/index.js | 8 ++--- .../use-inner-block-template-sync.js | 4 +-- .../hooks/use-clipboard-block.native.js | 6 ++-- .../inserter/hooks/use-insertion-point.js | 4 +-- .../src/components/inserter/menu.native.js | 4 +-- .../block-support-tools-panel.js | 4 +-- .../list-view/use-block-selection.js | 6 ++-- .../list-view/use-list-view-drop-zone.js | 4 +-- .../rich-text/use-caret-in-format.js | 4 +-- .../rich-text/use-undo-automatic-change.js | 6 ++-- .../components/use-block-drop-zone/index.js | 4 +-- .../src/components/use-on-block-drop/index.js | 4 +-- .../components/writing-flow/use-arrow-nav.js | 4 +-- .../components/writing-flow/use-select-all.js | 4 +-- .../components/writing-flow/use-tab-nav.js | 4 +-- .../block-library/src/buttons/edit.native.js | 4 +-- .../src/embed/embed-preview.native.js | 4 +-- packages/block-library/src/freeform/edit.js | 4 +-- packages/block-library/src/image/image.js | 4 +-- .../use-clear-selected-block.js | 4 +-- packages/data/README.md | 12 +++++++ .../src/components/use-selectors/index.js | 35 +++++++++++++++++++ packages/data/src/index.js | 1 + .../header/template-title/delete-template.js | 4 +-- .../template-title/edit-template-title.js | 4 +-- .../components/keyboard-shortcuts/index.js | 6 ++-- .../editor/global-styles-renderer.js | 4 +-- .../global-styles/global-styles-provider.js | 4 +-- .../header/document-actions/index.js | 4 +-- .../components/keyboard-shortcuts/index.js | 6 ++-- .../convert-to-regular.js | 4 +-- .../save-shortcut.js | 10 +++--- .../local-autosave-monitor/index.js | 4 +-- .../data-no-store-string-literals.js | 2 +- .../src/hooks/use-shortcut-event-match.js | 6 ++-- 44 files changed, 143 insertions(+), 89 deletions(-) create mode 100644 packages/block-editor/src/components/block-list/use-block-props/use-multi-selection.js create mode 100644 packages/data/src/components/use-selectors/index.js diff --git a/packages/block-editor/src/components/block-actions/index.js b/packages/block-editor/src/components/block-actions/index.js index d9ca4acf5d1cd2..dcf14cd6a2d832 100644 --- a/packages/block-editor/src/components/block-actions/index.js +++ b/packages/block-editor/src/components/block-actions/index.js @@ -6,7 +6,7 @@ import { castArray, first, last, every } from 'lodash'; /** * WordPress dependencies */ -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; import { hasBlockSupport, switchToBlockType, @@ -30,8 +30,8 @@ export default function BlockActions( { getBlocksByClientId, canMoveBlocks, canRemoveBlocks, - } = useSelect( blockEditorStore ); - const { getDefaultBlockName, getGroupingBlockName } = useSelect( + } = useSelectors( blockEditorStore ); + const { getDefaultBlockName, getGroupingBlockName } = useSelectors( blocksStore ); diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-focus-handler.js b/packages/block-editor/src/components/block-list/use-block-props/use-focus-handler.js index 4e9ffa45725003..c3b28f6a9b17c6 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-focus-handler.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-focus-handler.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect, useDispatch } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; import { useRefEffect } from '@wordpress/compose'; /** @@ -16,7 +16,7 @@ import { store as blockEditorStore } from '../../../store'; * @param {string} clientId Block client ID. */ export function useFocusHandler( clientId ) { - const { isBlockSelected } = useSelect( blockEditorStore ); + const { isBlockSelected } = useSelectors( blockEditorStore ); const { selectBlock, selectionChange } = useDispatch( blockEditorStore ); return useRefEffect( diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-multi-selection.js b/packages/block-editor/src/components/block-list/use-block-props/use-multi-selection.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-nav-mode-exit.js b/packages/block-editor/src/components/block-list/use-block-props/use-nav-mode-exit.js index 7289751f515ae1..eb0edbb9937da3 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-nav-mode-exit.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-nav-mode-exit.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect, useDispatch } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; import { useRefEffect } from '@wordpress/compose'; /** @@ -15,7 +15,9 @@ import { store as blockEditorStore } from '../../../store'; * @param {string} clientId Block client ID. */ export function useNavModeExit( clientId ) { - const { isNavigationMode, isBlockSelected } = useSelect( blockEditorStore ); + const { isNavigationMode, isBlockSelected } = useSelectors( + blockEditorStore + ); const { setNavigationMode, selectBlock } = useDispatch( blockEditorStore ); return useRefEffect( ( node ) => { diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-selected-block-event-handlers.js b/packages/block-editor/src/components/block-list/use-block-props/use-selected-block-event-handlers.js index 8f3d5a5ec39539..56fa28d2ffd7b4 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-selected-block-event-handlers.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-selected-block-event-handlers.js @@ -3,7 +3,7 @@ */ import { isTextField } from '@wordpress/dom'; import { ENTER, BACKSPACE, DELETE } from '@wordpress/keycodes'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { useRefEffect } from '@wordpress/compose'; /** @@ -24,7 +24,7 @@ export function useEventHandlers( clientId ) { ( select ) => select( blockEditorStore ).isBlockSelected( clientId ), [ clientId ] ); - const { getBlockRootClientId, getBlockIndex } = useSelect( + const { getBlockRootClientId, getBlockIndex } = useSelectors( blockEditorStore ); const { insertDefaultBlock, removeBlock } = useDispatch( blockEditorStore ); diff --git a/packages/block-editor/src/components/block-list/use-in-between-inserter.js b/packages/block-editor/src/components/block-list/use-in-between-inserter.js index ad90940a0e657a..3e20488d391afe 100644 --- a/packages/block-editor/src/components/block-list/use-in-between-inserter.js +++ b/packages/block-editor/src/components/block-list/use-in-between-inserter.js @@ -3,7 +3,7 @@ */ import { useRefEffect } from '@wordpress/compose'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { useContext } from '@wordpress/element'; /** @@ -26,7 +26,7 @@ export function useInBetweenInserter() { isMultiSelecting, getSelectedBlockClientIds, getTemplateLock, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); const { showInsertionPoint, hideInsertionPoint } = useDispatch( blockEditorStore ); diff --git a/packages/block-editor/src/components/block-selection-clearer/index.js b/packages/block-editor/src/components/block-selection-clearer/index.js index acdb6669bff886..39611a866fed32 100644 --- a/packages/block-editor/src/components/block-selection-clearer/index.js +++ b/packages/block-editor/src/components/block-selection-clearer/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect, useDispatch } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; import { useRefEffect } from '@wordpress/compose'; /** @@ -17,7 +17,7 @@ import { store as blockEditorStore } from '../../store'; * @return {import('react').RefCallback} Ref callback. */ export function useBlockSelectionClearer() { - const { hasSelectedBlock, hasMultiSelection } = useSelect( + const { hasSelectedBlock, hasMultiSelection } = useSelectors( blockEditorStore ); const { clearSelectedBlock } = useDispatch( blockEditorStore ); diff --git a/packages/block-editor/src/components/block-tools/block-selection-button.js b/packages/block-editor/src/components/block-tools/block-selection-button.js index 73805544c7f7af..fded602279fa56 100644 --- a/packages/block-editor/src/components/block-tools/block-selection-button.js +++ b/packages/block-editor/src/components/block-tools/block-selection-button.js @@ -8,7 +8,7 @@ import classnames from 'classnames'; */ import { dragHandle } from '@wordpress/icons'; import { Button, Flex, FlexItem } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { useEffect, useRef } from '@wordpress/element'; import { BACKSPACE, @@ -101,7 +101,7 @@ function BlockSelectionButton( { clientId, rootClientId, blockElement } ) { getPreviousBlockClientId, getNextBlockClientId, isNavigationMode, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); const { selectBlock, clearSelectedBlock, diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index d581a43dae4a8f..941afb1206ba28 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -6,7 +6,7 @@ import { first, last } from 'lodash'; /** * WordPress dependencies */ -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { useViewportMatch } from '@wordpress/compose'; import { Popover } from '@wordpress/components'; import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts'; @@ -40,7 +40,7 @@ export default function BlockTools( { [] ); const isMatch = useShortcutEventMatch(); - const { getSelectedBlockClientIds, getBlockRootClientId } = useSelect( + const { getSelectedBlockClientIds, getBlockRootClientId } = useSelectors( blockEditorStore ); const { diff --git a/packages/block-editor/src/components/copy-handler/index.js b/packages/block-editor/src/components/copy-handler/index.js index 5ae3e3b13c8bd7..80c9fb3d5b682e 100644 --- a/packages/block-editor/src/components/copy-handler/index.js +++ b/packages/block-editor/src/components/copy-handler/index.js @@ -11,7 +11,7 @@ import { documentHasSelection, documentHasUncollapsedSelection, } from '@wordpress/dom'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; import { __, _n, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useRefEffect } from '@wordpress/compose'; @@ -23,8 +23,8 @@ import { getPasteEventData } from '../../utils/pasting'; import { store as blockEditorStore } from '../../store'; export function useNotifyCopy() { - const { getBlockName } = useSelect( blockEditorStore ); - const { getBlockType } = useSelect( blocksStore ); + const { getBlockName } = useSelectors( blockEditorStore ); + const { getBlockType } = useSelectors( blocksStore ); const { createSuccessNotice } = useDispatch( noticesStore ); return useCallback( ( eventType, selectedBlockClientIds ) => { @@ -78,7 +78,7 @@ export function useClipboardHandler() { getSelectedBlockClientIds, hasMultiSelection, getSettings, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); const { flashBlock, removeBlocks, replaceBlocks } = useDispatch( blockEditorStore ); diff --git a/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js b/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js index de9d3fa9b5849b..db26ed45f57abd 100644 --- a/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js +++ b/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js @@ -7,7 +7,7 @@ import { isEqual } from 'lodash'; * WordPress dependencies */ import { useRef, useLayoutEffect } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { synchronizeBlocksWithTemplate } from '@wordpress/blocks'; /** @@ -40,7 +40,7 @@ export default function useInnerBlockTemplateSync( templateLock, templateInsertUpdatesSelection ) { - const { getSelectedBlocksInitialCaretPosition } = useSelect( + const { getSelectedBlocksInitialCaretPosition } = useSelectors( blockEditorStore ); const { replaceInnerBlocks } = useDispatch( blockEditorStore ); diff --git a/packages/block-editor/src/components/inserter/hooks/use-clipboard-block.native.js b/packages/block-editor/src/components/inserter/hooks/use-clipboard-block.native.js index 7b0d78feb335c9..68198d371c8779 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-clipboard-block.native.js +++ b/packages/block-editor/src/components/inserter/hooks/use-clipboard-block.native.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useSelectors } from '@wordpress/data'; import { rawHandler, store as blocksStore } from '@wordpress/blocks'; import { getClipboard } from '@wordpress/components'; @@ -11,8 +11,8 @@ import { getClipboard } from '@wordpress/components'; import { store as blockEditorStore } from '../../../store'; export default function useClipboardBlock( destinationRootClientId ) { - const { canInsertBlockType } = useSelect( blockEditorStore ); - const { getBlockType } = useSelect( blocksStore ); + const { canInsertBlockType } = useSelectors( blockEditorStore ); + const { getBlockType } = useSelectors( blocksStore ); const clipboard = getClipboard(); const clipboardBlock = rawHandler( { HTML: clipboard } )[ 0 ]; diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index dd2cc30db9056d..19f227c0ef1e88 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -6,7 +6,7 @@ import { castArray } from 'lodash'; /** * WordPress dependencies */ -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelect, useSelectors } from '@wordpress/data'; import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; import { _n, sprintf } from '@wordpress/i18n'; import { speak } from '@wordpress/a11y'; @@ -45,7 +45,7 @@ function useInsertionPoint( { onSelect, shouldFocusBlock = true, } ) { - const { getSelectedBlock } = useSelect( blockEditorStore ); + const { getSelectedBlock } = useSelectors( blockEditorStore ); const { destinationRootClientId, destinationIndex } = useSelect( ( select ) => { const { diff --git a/packages/block-editor/src/components/inserter/menu.native.js b/packages/block-editor/src/components/inserter/menu.native.js index 17c3176eb64d5a..8cb1076fbe5b76 100644 --- a/packages/block-editor/src/components/inserter/menu.native.js +++ b/packages/block-editor/src/components/inserter/menu.native.js @@ -7,7 +7,7 @@ import { AccessibilityInfo, TouchableHighlight, Platform } from 'react-native'; * WordPress dependencies */ import { useEffect, useState, useCallback } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { createBlock } from '@wordpress/blocks'; import { BottomSheet, @@ -80,7 +80,7 @@ function InserterMenu( { } ); - const { getBlockOrder, getBlockCount } = useSelect( blockEditorStore ); + const { getBlockOrder, getBlockCount } = useSelectors( blockEditorStore ); useEffect( () => { // Show/Hide insertion point on Mount/Dismount diff --git a/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.js b/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.js index d582548b5d64ef..83e56b61162159 100644 --- a/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.js +++ b/packages/block-editor/src/components/inspector-controls/block-support-tools-panel.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __experimentalToolsPanel as ToolsPanel } from '@wordpress/components'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; import { useCallback } from '@wordpress/element'; /** @@ -18,7 +18,7 @@ export default function BlockSupportToolsPanel( { children, group, label } ) { getMultiSelectedBlockClientIds, getSelectedBlockClientId, hasMultiSelection, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); const panelId = getSelectedBlockClientId(); const resetAll = useCallback( diff --git a/packages/block-editor/src/components/list-view/use-block-selection.js b/packages/block-editor/src/components/list-view/use-block-selection.js index 2edcd3ed8bad3d..a303a30ac066f7 100644 --- a/packages/block-editor/src/components/list-view/use-block-selection.js +++ b/packages/block-editor/src/components/list-view/use-block-selection.js @@ -8,7 +8,7 @@ import { difference } from 'lodash'; */ import { speak } from '@wordpress/a11y'; import { __, sprintf } from '@wordpress/i18n'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; import { useCallback } from '@wordpress/element'; import { UP, DOWN, HOME, END } from '@wordpress/keycodes'; import { store as blocksStore } from '@wordpress/blocks'; @@ -31,9 +31,9 @@ export default function useBlockSelection() { getSelectedBlockClientIds, hasMultiSelection, hasSelectedBlock, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); - const { getBlockType } = useSelect( blocksStore ); + const { getBlockType } = useSelectors( blocksStore ); const updateBlockSelection = useCallback( async ( event, clientId, destinationClientId ) => { diff --git a/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js b/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js index 6a0baa21d09b3f..0aabc980b2162c 100644 --- a/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js +++ b/packages/block-editor/src/components/list-view/use-list-view-drop-zone.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useSelectors } from '@wordpress/data'; import { useState, useCallback } from '@wordpress/element'; import { useThrottle, @@ -201,7 +201,7 @@ export default function useListViewDropZone() { getBlockCount, getDraggedBlockClientIds, canInsertBlocks, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); const [ target, setTarget ] = useState(); const { rootClientId: targetRootClientId, blockIndex: targetBlockIndex } = target || {}; diff --git a/packages/block-editor/src/components/rich-text/use-caret-in-format.js b/packages/block-editor/src/components/rich-text/use-caret-in-format.js index f5ad69f39713b7..920159889d3f9f 100644 --- a/packages/block-editor/src/components/rich-text/use-caret-in-format.js +++ b/packages/block-editor/src/components/rich-text/use-caret-in-format.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { useEffect } from '@wordpress/element'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; /** * Internal dependencies @@ -12,7 +12,7 @@ import { store as blockEditorStore } from '../../store'; export function useCaretInFormat( { value } ) { const hasActiveFormats = value.activeFormats && !! value.activeFormats.length; - const { isCaretWithinFormattedText } = useSelect( blockEditorStore ); + const { isCaretWithinFormattedText } = useSelectors( blockEditorStore ); const { enterFormattedText, exitFormattedText } = useDispatch( blockEditorStore ); diff --git a/packages/block-editor/src/components/rich-text/use-undo-automatic-change.js b/packages/block-editor/src/components/rich-text/use-undo-automatic-change.js index 890746cec74a35..3719310e71d608 100644 --- a/packages/block-editor/src/components/rich-text/use-undo-automatic-change.js +++ b/packages/block-editor/src/components/rich-text/use-undo-automatic-change.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useSelectors } from '@wordpress/data'; import { useRefEffect } from '@wordpress/compose'; import { BACKSPACE, DELETE, ESCAPE } from '@wordpress/keycodes'; @@ -11,7 +11,9 @@ import { BACKSPACE, DELETE, ESCAPE } from '@wordpress/keycodes'; import { store as blockEditorStore } from '../../store'; export function useUndoAutomaticChange() { - const { didAutomaticChange, getSettings } = useSelect( blockEditorStore ); + const { didAutomaticChange, getSettings } = useSelectors( + blockEditorStore + ); return useRefEffect( ( element ) => { function onKeyDown( event ) { const { keyCode } = event; diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index f9edab72733537..a0c10e0480e491 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelect, useSelectors } from '@wordpress/data'; import { useCallback, useState } from '@wordpress/element'; import { useThrottle, @@ -100,7 +100,7 @@ export default function useBlockDropZone( { [ targetRootClientId ] ); - const { getBlockListSettings } = useSelect( blockEditorStore ); + const { getBlockListSettings } = useSelectors( blockEditorStore ); const { showInsertionPoint, hideInsertionPoint } = useDispatch( blockEditorStore ); diff --git a/packages/block-editor/src/components/use-on-block-drop/index.js b/packages/block-editor/src/components/use-on-block-drop/index.js index 76dbb119912a73..6446bd74db7613 100644 --- a/packages/block-editor/src/components/use-on-block-drop/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/index.js @@ -7,7 +7,7 @@ import { getBlockTransforms, pasteHandler, } from '@wordpress/blocks'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelect, useSelectors } from '@wordpress/data'; import { getFilesFromDataTransfer } from '@wordpress/dom'; /** @@ -221,7 +221,7 @@ export default function useOnBlockDrop( targetRootClientId, targetBlockIndex ) { canInsertBlockType, getBlockIndex, getClientIdsOfDescendants, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); const { insertBlocks, moveBlocksToPosition, diff --git a/packages/block-editor/src/components/writing-flow/use-arrow-nav.js b/packages/block-editor/src/components/writing-flow/use-arrow-nav.js index d02233650c392f..7efbda3bfaeebb 100644 --- a/packages/block-editor/src/components/writing-flow/use-arrow-nav.js +++ b/packages/block-editor/src/components/writing-flow/use-arrow-nav.js @@ -16,7 +16,7 @@ import { isRTL, } from '@wordpress/dom'; import { UP, DOWN, LEFT, RIGHT } from '@wordpress/keycodes'; -import { useSelect } from '@wordpress/data'; +import { useSelectors } from '@wordpress/data'; import { useRefEffect } from '@wordpress/compose'; /** @@ -125,7 +125,7 @@ export default function useArrowNav() { getNextBlockClientId, getSettings, hasMultiSelection, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); return useRefEffect( ( node ) => { // Here a DOMRect is stored while moving the caret vertically so // vertical position of the start position can be restored. This is to diff --git a/packages/block-editor/src/components/writing-flow/use-select-all.js b/packages/block-editor/src/components/writing-flow/use-select-all.js index 20c295912a7f7c..90539bcea00b32 100644 --- a/packages/block-editor/src/components/writing-flow/use-select-all.js +++ b/packages/block-editor/src/components/writing-flow/use-select-all.js @@ -7,7 +7,7 @@ import { first, last } from 'lodash'; * WordPress dependencies */ import { isEntirelySelected } from '@wordpress/dom'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts'; import { useRefEffect } from '@wordpress/compose'; @@ -21,7 +21,7 @@ export default function useSelectAll() { getBlockOrder, getSelectedBlockClientIds, getBlockRootClientId, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); const { multiSelect } = useDispatch( blockEditorStore ); const isMatch = useShortcutEventMatch(); diff --git a/packages/block-editor/src/components/writing-flow/use-tab-nav.js b/packages/block-editor/src/components/writing-flow/use-tab-nav.js index 61697a4c0d2beb..21a6e5bf60a5f6 100644 --- a/packages/block-editor/src/components/writing-flow/use-tab-nav.js +++ b/packages/block-editor/src/components/writing-flow/use-tab-nav.js @@ -3,7 +3,7 @@ */ import { focus, isFormElement } from '@wordpress/dom'; import { TAB, ESCAPE } from '@wordpress/keycodes'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { useRefEffect, useMergeRefs } from '@wordpress/compose'; import { useRef } from '@wordpress/element'; @@ -21,7 +21,7 @@ export default function useTabNav() { hasMultiSelection, getSelectedBlockClientId, getBlockCount, - } = useSelect( blockEditorStore ); + } = useSelectors( blockEditorStore ); const { setNavigationMode } = useDispatch( blockEditorStore ); const isNavigationMode = useSelect( ( select ) => select( blockEditorStore ).isNavigationMode(), diff --git a/packages/block-library/src/buttons/edit.native.js b/packages/block-library/src/buttons/edit.native.js index fe6186f1278b8f..9352097899f6e3 100644 --- a/packages/block-library/src/buttons/edit.native.js +++ b/packages/block-library/src/buttons/edit.native.js @@ -15,7 +15,7 @@ import { } from '@wordpress/block-editor'; import { createBlock, getBlockSupport } from '@wordpress/blocks'; import { useResizeObserver } from '@wordpress/compose'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelect, useSelectors } from '@wordpress/data'; import { useState, useEffect, useRef, useCallback } from '@wordpress/element'; import { alignmentHelpers } from '@wordpress/components'; @@ -79,7 +79,7 @@ export default function ButtonsEdit( { return preferredStyleVariations?.value?.[ buttonBlockName ]; }, [] ); - const { getBlockOrder } = useSelect( blockEditorStore ); + const { getBlockOrder } = useSelectors( blockEditorStore ); const { insertBlock, removeBlock, selectBlock } = useDispatch( blockEditorStore ); diff --git a/packages/block-library/src/embed/embed-preview.native.js b/packages/block-library/src/embed/embed-preview.native.js index bb8df663baf281..d67fa70332badd 100644 --- a/packages/block-library/src/embed/embed-preview.native.js +++ b/packages/block-library/src/embed/embed-preview.native.js @@ -16,7 +16,7 @@ import { import { __, sprintf } from '@wordpress/i18n'; import { memo, useState } from '@wordpress/element'; import { SandBox } from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; +import { useSelectors } from '@wordpress/data'; /** * Internal dependencies @@ -43,7 +43,7 @@ const EmbedPreview = ( { isDefaultEmbedInfo, } ) => { const [ isCaptionSelected, setIsCaptionSelected ] = useState( false ); - const { locale } = useSelect( blockEditorStore ).getSettings(); + const { locale } = useSelectors( blockEditorStore ).getSettings(); const wrapperStyle = styles[ 'embed-preview__wrapper' ]; const wrapperAlignStyle = diff --git a/packages/block-library/src/freeform/edit.js b/packages/block-library/src/freeform/edit.js index 4530fbec0a59bd..4799f5b61801b2 100644 --- a/packages/block-library/src/freeform/edit.js +++ b/packages/block-library/src/freeform/edit.js @@ -11,7 +11,7 @@ import { useBlockProps, store as blockEditorStore, } from '@wordpress/block-editor'; -import { useSelect } from '@wordpress/data'; +import { useSelectors } from '@wordpress/data'; import { ToolbarGroup } from '@wordpress/components'; import { useEffect, useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -46,7 +46,7 @@ export default function ClassicEdit( { setAttributes, onReplace, } ) { - const { getMultiSelectedBlockClientIds } = useSelect( blockEditorStore ); + const { getMultiSelectedBlockClientIds } = useSelectors( blockEditorStore ); const didMount = useRef( false ); useEffect( () => { diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 0fa472afca503c..cc4ace88f8e4ca 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -17,7 +17,7 @@ import { ToolbarButton, } from '@wordpress/components'; import { useViewportMatch, usePrevious } from '@wordpress/compose'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { BlockControls, InspectorControls, @@ -85,7 +85,7 @@ export default function Image( { const captionRef = useRef(); const prevUrl = usePrevious( url ); const { allowResize = true } = context; - const { getBlock } = useSelect( blockEditorStore ); + const { getBlock } = useSelectors( blockEditorStore ); const { image, multiImageSelection } = useSelect( ( select ) => { diff --git a/packages/customize-widgets/src/components/customize-widgets/use-clear-selected-block.js b/packages/customize-widgets/src/components/customize-widgets/use-clear-selected-block.js index cf9d263fb18b38..db5c45973799f8 100644 --- a/packages/customize-widgets/src/components/customize-widgets/use-clear-selected-block.js +++ b/packages/customize-widgets/src/components/customize-widgets/use-clear-selected-block.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { useEffect } from '@wordpress/element'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useSelectors, useDispatch } from '@wordpress/data'; import { store as blockEditorStore } from '@wordpress/block-editor'; /** @@ -22,7 +22,7 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; * @param {Object} popoverRef The ref object of the popover node container. */ export default function useClearSelectedBlock( sidebarControl, popoverRef ) { - const { hasSelectedBlock, hasMultiSelection } = useSelect( + const { hasSelectedBlock, hasMultiSelection } = useSelectors( blockEditorStore ); const { clearSelectedBlock } = useDispatch( blockEditorStore ); diff --git a/packages/data/README.md b/packages/data/README.md index 2e3e07a13ecc1b..73faca099cb82c 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -827,6 +827,18 @@ _Returns_ - `Function`: A custom react hook. +### useSelectors + +Retrieve the controls of a store, so that it can be used to get data in event callbacks. + +_Parameters_ + +- _storeName_ `string`: Key of the store to get controls for. **Don't use `useSelect` for calling the selectors in the render function because your component won't re-render on a data change. You need to use useSelect in that case.** `js import { useSelect } from '@wordpress/data'; function Paste( { children } ) { const { getSettings } = useSelect( 'my-shop' ); function onPaste() { // Do something with the settings. const settings = getSettings(); } return
{ children }
; }` + +_Returns_ + +- `Object`: The store's selectors. + ### withDispatch Higher-order component used to add dispatch props using registered action diff --git a/packages/data/src/components/use-selectors/index.js b/packages/data/src/components/use-selectors/index.js new file mode 100644 index 00000000000000..f03c8debeba0d3 --- /dev/null +++ b/packages/data/src/components/use-selectors/index.js @@ -0,0 +1,35 @@ +/** + * Internal dependencies + */ +import useRegistry from '../registry-provider/use-registry'; + +/** + * + * Retrieve the controls of a store, so that it can be used to get data in event callbacks. + * + * @param {string} storeName Key of the store to get controls for. + * + * **Don't use `useSelect` for calling the selectors in the render + * function because your component won't re-render on a data change. + * You need to use useSelect in that case.** + * + * ```js + * import { useSelect } from '@wordpress/data'; + * + * function Paste( { children } ) { + * const { getSettings } = useSelect( 'my-shop' ); + * function onPaste() { + * // Do something with the settings. + * const settings = getSettings(); + * } + * return
{ children }
; + * } + * ``` + * + * @return {Object} The store's selectors. + */ +export default function useSelectors( storeName ) { + const registry = useRegistry(); + + return registry.select( storeName ); +} diff --git a/packages/data/src/index.js b/packages/data/src/index.js index 0dc98955f212c2..1ccbf9dd36a6e5 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -20,6 +20,7 @@ export { useRegistry, } from './components/registry-provider'; export { default as useSelect } from './components/use-select'; +export { default as useSelectors } from './components/use-selectors'; export { useDispatch } from './components/use-dispatch'; export { AsyncModeProvider } from './components/async-mode-provider'; export { createRegistry } from './registry'; diff --git a/packages/edit-post/src/components/header/template-title/delete-template.js b/packages/edit-post/src/components/header/template-title/delete-template.js index c550b552bbd7e7..4fd3c26c112196 100644 --- a/packages/edit-post/src/components/header/template-title/delete-template.js +++ b/packages/edit-post/src/components/header/template-title/delete-template.js @@ -13,7 +13,7 @@ import { __experimentalConfirmDialog as ConfirmDialog, } from '@wordpress/components'; import { store as blockEditorStore } from '@wordpress/block-editor'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelect, useSelectors } from '@wordpress/data'; import { store as editorStore } from '@wordpress/editor'; import { store as coreStore } from '@wordpress/core-data'; import { useState } from '@wordpress/element'; @@ -26,7 +26,7 @@ import { store as editPostStore } from '../../../store'; export default function DeleteTemplate() { const { clearSelectedBlock } = useDispatch( blockEditorStore ); const { setIsEditingTemplate } = useDispatch( editPostStore ); - const { getEditorSettings } = useSelect( editorStore ); + const { getEditorSettings } = useSelectors( editorStore ); const { updateEditorSettings, editPost } = useDispatch( editorStore ); const { deleteEntityRecord } = useDispatch( coreStore ); const { template } = useSelect( ( select ) => { diff --git a/packages/edit-post/src/components/header/template-title/edit-template-title.js b/packages/edit-post/src/components/header/template-title/edit-template-title.js index 2865e674b341fd..de21b44d650f8c 100644 --- a/packages/edit-post/src/components/header/template-title/edit-template-title.js +++ b/packages/edit-post/src/components/header/template-title/edit-template-title.js @@ -8,7 +8,7 @@ import { mapValues } from 'lodash'; */ import { __ } from '@wordpress/i18n'; import { TextControl } from '@wordpress/components'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelect, useSelectors } from '@wordpress/data'; import { store as editorStore } from '@wordpress/editor'; import { store as coreStore } from '@wordpress/core-data'; @@ -26,7 +26,7 @@ export default function EditTemplateTitle() { }, [] ); const { editEntityRecord } = useDispatch( coreStore ); - const { getEditorSettings } = useSelect( editorStore ); + const { getEditorSettings } = useSelectors( editorStore ); const { updateEditorSettings } = useDispatch( editorStore ); if ( template.has_theme_file ) { diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js index 33b66961006c18..973cc21e019363 100644 --- a/packages/edit-post/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { useEffect } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { useShortcut, store as keyboardShortcutsStore, @@ -17,12 +17,12 @@ import { store as blockEditorStore } from '@wordpress/block-editor'; import { store as editPostStore } from '../../store'; function KeyboardShortcuts() { - const { getBlockSelectionStart } = useSelect( blockEditorStore ); + const { getBlockSelectionStart } = useSelectors( blockEditorStore ); const { getEditorMode, isEditorSidebarOpened, isListViewOpened, - } = useSelect( editPostStore ); + } = useSelectors( editPostStore ); const isModeToggleDisabled = useSelect( ( select ) => { const { richEditingEnabled, codeEditingEnabled } = select( editorStore diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js index 3abcb1813fd572..f407c0013a31c8 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -7,7 +7,7 @@ import { filter } from 'lodash'; * WordPress dependencies */ import { useEffect } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useDispatch, useSelectors } from '@wordpress/data'; /** * Internal dependencies @@ -21,7 +21,7 @@ import { useGlobalStylesOutput } from '../global-styles/use-global-styles-output function useGlobalStylesRenderer() { const [ styles, settings ] = useGlobalStylesOutput(); - const { getSettings } = useSelect( editSiteStore ); + const { getSettings } = useSelectors( editSiteStore ); const { updateSettings } = useDispatch( editSiteStore ); useEffect( () => { diff --git a/packages/edit-site/src/components/global-styles/global-styles-provider.js b/packages/edit-site/src/components/global-styles/global-styles-provider.js index e4f481b05b6f85..90111a802dbbc2 100644 --- a/packages/edit-site/src/components/global-styles/global-styles-provider.js +++ b/packages/edit-site/src/components/global-styles/global-styles-provider.js @@ -14,7 +14,7 @@ import { * WordPress dependencies */ import { useMemo, useCallback } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; /** @@ -65,7 +65,7 @@ function useGlobalStylesUserConfig() { }; }, [] ); - const { getEditedEntityRecord } = useSelect( coreStore ); + const { getEditedEntityRecord } = useSelectors( coreStore ); const { editEntityRecord } = useDispatch( coreStore ); const config = useMemo( () => { return { diff --git a/packages/edit-site/src/components/header/document-actions/index.js b/packages/edit-site/src/components/header/document-actions/index.js index 0c3e465aba410f..e15068b3efe3fd 100644 --- a/packages/edit-site/src/components/header/document-actions/index.js +++ b/packages/edit-site/src/components/header/document-actions/index.js @@ -11,7 +11,7 @@ import { __experimentalGetBlockLabel as getBlockLabel, getBlockType, } from '@wordpress/blocks'; -import { useSelect } from '@wordpress/data'; +import { useSelect, useSelectors } from '@wordpress/data'; import { Dropdown, Button, @@ -31,7 +31,7 @@ function getBlockDisplayText( block ) { } function useSecondaryText() { - const { getBlock } = useSelect( blockEditorStore ); + const { getBlock } = useSelectors( blockEditorStore ); const activeEntityBlockId = useSelect( ( select ) => select( diff --git a/packages/edit-site/src/components/keyboard-shortcuts/index.js b/packages/edit-site/src/components/keyboard-shortcuts/index.js index 9b6cb72906e641..e002c57d1bcabb 100644 --- a/packages/edit-site/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-site/src/components/keyboard-shortcuts/index.js @@ -6,7 +6,7 @@ import { useShortcut, store as keyboardShortcutsStore, } from '@wordpress/keyboard-shortcuts'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch, useSelect, useSelectors } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; import { store as interfaceStore } from '@wordpress/interface'; @@ -22,8 +22,8 @@ function KeyboardShortcuts( { openEntitiesSavedStates } ) { const { __experimentalGetDirtyEntityRecords, isSavingEntityRecord, - } = useSelect( coreStore ); - const { getEditorMode } = useSelect( editSiteStore ); + } = useSelectors( coreStore ); + const { getEditorMode } = useSelectors( editSiteStore ); const isListViewOpen = useSelect( ( select ) => select( editSiteStore ).isListViewOpened(), [] diff --git a/packages/edit-site/src/components/template-part-converter/convert-to-regular.js b/packages/edit-site/src/components/template-part-converter/convert-to-regular.js index d64cbc90737e24..e8b4202606db79 100644 --- a/packages/edit-site/src/components/template-part-converter/convert-to-regular.js +++ b/packages/edit-site/src/components/template-part-converter/convert-to-regular.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect, useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect, useSelectors } from '@wordpress/data'; import { BlockSettingsMenuControls, store as blockEditorStore, @@ -10,7 +10,7 @@ import { MenuItem } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; export default function ConvertToRegularBlocks( { clientId } ) { - const { getBlocks } = useSelect( blockEditorStore ); + const { getBlocks } = useSelectors( blockEditorStore ); const { replaceBlocks } = useDispatch( blockEditorStore ); const canRemove = useSelect( diff --git a/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js b/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js index d4ae0de4b6751a..e5e65cb0a2e899 100644 --- a/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js +++ b/packages/editor/src/components/global-keyboard-shortcuts/save-shortcut.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { useShortcut } from '@wordpress/keyboard-shortcuts'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useSelectors, useDispatch } from '@wordpress/data'; import { parse } from '@wordpress/blocks'; /** @@ -12,9 +12,11 @@ import { store as editorStore } from '../../store'; function SaveShortcut( { resetBlocksOnSave } ) { const { resetEditorBlocks, savePost } = useDispatch( editorStore ); - const { isEditedPostDirty, getPostEdits, isPostSavingLocked } = useSelect( - editorStore - ); + const { + isEditedPostDirty, + getPostEdits, + isPostSavingLocked, + } = useSelectors( editorStore ); useShortcut( 'core/editor/save', ( event ) => { event.preventDefault(); diff --git a/packages/editor/src/components/local-autosave-monitor/index.js b/packages/editor/src/components/local-autosave-monitor/index.js index e5fc9c6b8078cb..59e133435e573a 100644 --- a/packages/editor/src/components/local-autosave-monitor/index.js +++ b/packages/editor/src/components/local-autosave-monitor/index.js @@ -8,7 +8,7 @@ import { once, uniqueId, omit } from 'lodash'; */ import { useCallback, useEffect, useRef } from '@wordpress/element'; import { ifCondition, usePrevious } from '@wordpress/compose'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch, useSelectors } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { parse } from '@wordpress/blocks'; import { store as noticesStore } from '@wordpress/notices'; @@ -59,7 +59,7 @@ function useAutosaveNotice() { } ), [] ); - const { getEditedPostAttribute } = useSelect( editorStore ); + const { getEditedPostAttribute } = useSelectors( editorStore ); const { createWarningNotice, removeNotice } = useDispatch( noticesStore ); const { editPost, resetEditorBlocks } = useDispatch( editorStore ); diff --git a/packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js b/packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js index c112117b30bfa9..b8c12249376a0c 100644 --- a/packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js +++ b/packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js @@ -26,7 +26,7 @@ const valid = [ // Direct function calls. `import { useDispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; useDispatch( coreStore );`, `import { dispatch } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; dispatch( coreStore );`, - `import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; useSelect( coreStore );`, + `import { useSelectors } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; useSelectors( coreStore );`, `import { select } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; select( coreStore );`, `import { resolveSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; resolveSelect( coreStore );`, `import { resolveSelect as resolveSelectAlias } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; resolveSelectAlias( coreStore );`, diff --git a/packages/keyboard-shortcuts/src/hooks/use-shortcut-event-match.js b/packages/keyboard-shortcuts/src/hooks/use-shortcut-event-match.js index 15ad0d6a1f2976..a04f4762cd4135 100644 --- a/packages/keyboard-shortcuts/src/hooks/use-shortcut-event-match.js +++ b/packages/keyboard-shortcuts/src/hooks/use-shortcut-event-match.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useSelectors } from '@wordpress/data'; import { isKeyboardEvent } from '@wordpress/keycodes'; /** @@ -12,11 +12,11 @@ import { store as keyboardShortcutsStore } from '../store'; /** * Returns a function to check if a keyboard event matches a shortcut name. * - * @return {Function} A function to to check if a keyboard event matches a + * @return {Function} A function to check if a keyboard event matches a * predefined shortcut combination. */ export default function useShortcutEventMatch() { - const { getAllShortcutKeyCombinations } = useSelect( + const { getAllShortcutKeyCombinations } = useSelectors( keyboardShortcutsStore ); From a7140b91cb26b0a401760feabeae4608eecf9d9a Mon Sep 17 00:00:00 2001 From: inwerpsel Date: Mon, 11 Apr 2022 18:17:17 +0200 Subject: [PATCH 2/2] Simplify useSelect by dispatching to useSelectors This is a deliberate rules-of-hooks violation that is safe to do because the condition stays same for each invocation, mapSelect's type never changes. Unfortunately it meant disabling the rules-of-hooks check in the whole function body, but there's no getting around that. However the chance for the rule to be needed in this function is rather small. --- packages/data/README.md | 7 +- .../data/src/components/use-select/index.js | 89 +++++++------------ 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/packages/data/README.md b/packages/data/README.md index 73faca099cb82c..71f741e995336d 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -800,7 +800,8 @@ doesn't change and other props are passed in that do change, the price will not change because the dependency is just the currency. When data is only used in an event callback, the data should not be retrieved -on render, so it may be useful to get the selectors function instead. +on render, so you need to use useSelectors instead. For backwards compatibility +only, this function still supports getting the selectors by passing a store. **Don't use `useSelect` this way when calling the selectors in the render function because your component won't re-render on a data change.** @@ -820,12 +821,12 @@ function Paste( { children } ) { _Parameters_ -- _mapSelect_ `Function|StoreDescriptor|string`: Function called on every state change. The returned value is exposed to the component implementing this hook. The function receives the `registry.select` method on the first argument and the `registry` on the second argument. When a store key is passed, all selectors for the store will be returned. This is only meant for usage of these selectors in event callbacks, not for data needed to create the element tree. +- _mapSelect_ `Function|StoreDescriptor|string`: Function called on every state change. The returned value is exposed to the component implementing this hook. The function receives the `registry.select` method on the first argument and the `registry` on the second argument. (deprecated) When a store key is passed, all selectors for the store will be returned. This is only meant for usage of these selectors in event callbacks, not for data needed to create the element tree. - _deps_ `Array`: If provided, this memoizes the mapSelect so the same `mapSelect` is invoked on every state change unless the dependencies change. _Returns_ -- `Function`: A custom react hook. +- `any`: The current map output or a store's selectors. ### useSelectors diff --git a/packages/data/src/components/use-select/index.js b/packages/data/src/components/use-select/index.js index cc204f19ef47b1..dcd7db7d1df02a 100644 --- a/packages/data/src/components/use-select/index.js +++ b/packages/data/src/components/use-select/index.js @@ -16,8 +16,8 @@ import { useIsomorphicLayoutEffect } from '@wordpress/compose'; */ import useRegistry from '../registry-provider/use-registry'; import useAsyncMode from '../async-mode-provider/use-async-mode'; +import { useSelectors } from '@wordpress/data'; -const noop = () => {}; const renderQueue = createQueue(); /** @typedef {import('../../types').StoreDescriptor} StoreDescriptor */ @@ -34,11 +34,11 @@ const renderQueue = createQueue(); * the `registry.select` method on the first * argument and the `registry` on the second * argument. - * When a store key is passed, all selectors for - * the store will be returned. This is only meant - * for usage of these selectors in event - * callbacks, not for data needed to create the - * element tree. + * (deprecated) When a store key is passed, all + * selectors for the store will be returned. This + * is only meant for usage of these selectors in + * event callbacks, not for data needed to create + * the element tree. * @param {Array} deps If provided, this memoizes the mapSelect so the * same `mapSelect` is invoked on every state * change unless the dependencies change. @@ -69,7 +69,8 @@ const renderQueue = createQueue(); * not change because the dependency is just the currency. * * When data is only used in an event callback, the data should not be retrieved - * on render, so it may be useful to get the selectors function instead. + * on render, so you need to use useSelectors instead. For backwards compatibility + * only, this function still supports getting the selectors by passing a store. * * **Don't use `useSelect` this way when calling the selectors in the render * function because your component won't re-render on a data change.** @@ -87,29 +88,17 @@ const renderQueue = createQueue(); * } * ``` * - * @return {Function} A custom react hook. + * @return {any} The current map output or a store's selectors. */ export default function useSelect( mapSelect, deps ) { - const hasMappingFunction = 'function' === typeof mapSelect; - - // If we're recalling a store by its name or by - // its descriptor then we won't be caching the - // calls to `mapSelect` because we won't be calling it. - if ( ! hasMappingFunction ) { - deps = []; + // This is a deliberate rules-of-hooks violation that is safe to do because mapSelect's type stays stable over time. + // In almost any other case you should never do this in a hook. + // The rule needs to be disabled inside the entire function body. + /* eslint-disable react-hooks/rules-of-hooks */ + if ( typeof mapSelect !== 'function' ) { + return useSelectors( mapSelect ); } - - // Because of the "rule of hooks" we have to call `useCallback` - // on every invocation whether or not we have a real function - // for `mapSelect`. we'll create this intermediate variable to - // fulfill that need and then reference it with our "real" - // `_mapSelect` if we can. - const callbackMapper = useCallback( - hasMappingFunction ? mapSelect : noop, - deps - ); - const _mapSelect = hasMappingFunction ? callbackMapper : null; - + const _mapSelect = useCallback( mapSelect, deps ); const registry = useRegistry(); const isAsync = useAsyncMode(); // React can sometimes clear the `useMemo` cache. @@ -141,36 +130,29 @@ export default function useSelect( mapSelect, deps ) { // in that case, we would still want to memoize it. const depsChangedFlag = useMemo( () => ( {} ), deps || [] ); - let mapOutput; - - if ( _mapSelect ) { - mapOutput = latestMapOutput.current; - const hasReplacedMapSelect = latestMapSelect.current !== _mapSelect; - const lastMapSelectFailed = !! latestMapOutputError.current; + let mapOutput = latestMapOutput.current; - if ( hasReplacedMapSelect || lastMapSelectFailed ) { - try { - mapOutput = wrapSelect( _mapSelect ); - } catch ( error ) { - let errorMessage = `An error occurred while running 'mapSelect': ${ error.message }`; + const hasReplacedMapSelect = latestMapSelect.current !== _mapSelect; + const lastMapSelectFailed = !! latestMapOutputError.current; - if ( latestMapOutputError.current ) { - errorMessage += `\nThe error may be correlated with this previous error:\n`; - errorMessage += `${ latestMapOutputError.current.stack }\n\n`; - errorMessage += 'Original stack trace:'; - } + if ( hasReplacedMapSelect || lastMapSelectFailed ) { + try { + mapOutput = wrapSelect( _mapSelect ); + } catch ( error ) { + let errorMessage = `An error occurred while running 'mapSelect': ${ error.message }`; - // eslint-disable-next-line no-console - console.error( errorMessage ); + if ( latestMapOutputError.current ) { + errorMessage += `\nThe error may be correlated with this previous error:\n`; + errorMessage += `${ latestMapOutputError.current.stack }\n\n`; + errorMessage += 'Original stack trace:'; } + + // eslint-disable-next-line no-console + console.error( errorMessage ); } } useIsomorphicLayoutEffect( () => { - if ( ! hasMappingFunction ) { - return; - } - latestMapSelect.current = _mapSelect; latestMapOutput.current = mapOutput; latestMapOutputError.current = undefined; @@ -187,10 +169,6 @@ export default function useSelect( mapSelect, deps ) { } ); useIsomorphicLayoutEffect( () => { - if ( ! hasMappingFunction ) { - return; - } - const onStoreChange = () => { if ( isMountedAndNotUnsubscribing.current ) { try { @@ -234,7 +212,8 @@ export default function useSelect( mapSelect, deps ) { // If you're tempted to eliminate the spread dependencies below don't do it! // We're passing these in from the calling function and want to make sure we're // examining every individual value inside the `deps` array. - }, [ registry, wrapSelect, hasMappingFunction, depsChangedFlag ] ); + }, [ registry, wrapSelect, depsChangedFlag ] ); - return hasMappingFunction ? mapOutput : registry.select( mapSelect ); + return mapOutput; + /* eslint-enable react-hooks/rules-of-hooks */ }