-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Block Editor: Add a new hook for getting a stable block context object #47028
Changes from all commits
572587e
7ceabc9
2a4af31
1904d75
71e5776
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,7 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useSelect } from '@wordpress/data'; | ||
import { | ||
getBlockType, | ||
__unstableGetInnerBlocksProps as getInnerBlocksProps, | ||
} from '@wordpress/blocks'; | ||
import { __unstableGetInnerBlocksProps as getInnerBlocksProps } from '@wordpress/blocks'; | ||
import { useRef } from '@wordpress/element'; | ||
|
||
/** | ||
|
@@ -15,7 +11,7 @@ import ButtonBlockAppender from './button-block-appender'; | |
import DefaultBlockAppender from './default-block-appender'; | ||
import useNestedSettingsUpdate from './use-nested-settings-update'; | ||
import useInnerBlockTemplateSync from './use-inner-block-template-sync'; | ||
import getBlockContext from './get-block-context'; | ||
import useBlockContext from './use-block-context'; | ||
|
||
/** | ||
* Internal dependencies | ||
|
@@ -26,7 +22,6 @@ import { useBlockEditContext } from '../block-edit/context'; | |
import useBlockSync from '../provider/use-block-sync'; | ||
import { BlockContextProvider } from '../block-context'; | ||
import { defaultLayout, LayoutProvider } from '../block-list/layout'; | ||
import { store as blockEditorStore } from '../../store'; | ||
|
||
/** | ||
* This hook is used to lightly mark an element as an inner blocks wrapper | ||
|
@@ -100,10 +95,7 @@ function UncontrolledInnerBlocks( props ) { | |
useCompactList, | ||
} = props; | ||
|
||
const block = useSelect( | ||
( select ) => select( blockEditorStore ).getBlock( clientId ), | ||
[ clientId ] | ||
) || { innerBlocks: [] }; | ||
const context = useBlockContext( clientId ); | ||
|
||
useNestedSettingsUpdate( clientId, allowedBlocks, templateLock ); | ||
|
||
|
@@ -116,43 +108,31 @@ function UncontrolledInnerBlocks( props ) { | |
|
||
const BlockListComponent = useCompactList ? BlockListCompact : BlockList; | ||
|
||
let blockList = ( | ||
<BlockListComponent | ||
marginVertical={ marginVertical } | ||
marginHorizontal={ marginHorizontal } | ||
rootClientId={ clientId } | ||
renderAppender={ renderAppender } | ||
renderFooterAppender={ renderFooterAppender } | ||
withFooter={ false } | ||
orientation={ orientation } | ||
parentWidth={ parentWidth } | ||
horizontalAlignment={ horizontalAlignment } | ||
horizontal={ horizontal } | ||
contentResizeMode={ contentResizeMode } | ||
contentStyle={ contentStyle } | ||
onAddBlock={ onAddBlock } | ||
onDeleteBlock={ onDeleteBlock } | ||
filterInnerBlocks={ filterInnerBlocks } | ||
gridProperties={ gridProperties } | ||
blockWidth={ blockWidth } | ||
/> | ||
return ( | ||
<LayoutProvider value={ layout }> | ||
<BlockContextProvider value={ context }> | ||
<BlockListComponent | ||
marginVertical={ marginVertical } | ||
marginHorizontal={ marginHorizontal } | ||
rootClientId={ clientId } | ||
renderAppender={ renderAppender } | ||
renderFooterAppender={ renderFooterAppender } | ||
withFooter={ false } | ||
orientation={ orientation } | ||
parentWidth={ parentWidth } | ||
horizontalAlignment={ horizontalAlignment } | ||
horizontal={ horizontal } | ||
contentResizeMode={ contentResizeMode } | ||
contentStyle={ contentStyle } | ||
onAddBlock={ onAddBlock } | ||
onDeleteBlock={ onDeleteBlock } | ||
filterInnerBlocks={ filterInnerBlocks } | ||
gridProperties={ gridProperties } | ||
blockWidth={ blockWidth } | ||
/> | ||
</BlockContextProvider> | ||
</LayoutProvider> | ||
); | ||
|
||
// Wrap context provider if (and only if) block has context to provide. | ||
const blockType = getBlockType( block.name ); | ||
if ( blockType && blockType.providesContext ) { | ||
const context = getBlockContext( block.attributes, blockType ); | ||
|
||
blockList = ( | ||
<LayoutProvider value={ layout }> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems to me you fixed a bug here: previously, if the component didn't provide a context, but did provide a layout, the I see quite a few blocks, very common ones, that are like this, i.e., their There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sort of. This was only working because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yes, so it was only potential, not real bug 👍 |
||
<BlockContextProvider value={ context }> | ||
{ blockList } | ||
</BlockContextProvider> | ||
</LayoutProvider> | ||
); | ||
} | ||
|
||
return blockList; | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { store as blocksStore } from '@wordpress/blocks'; | ||
import { useSelect } from '@wordpress/data'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { store as blockEditorStore } from '../../store'; | ||
|
||
/** | ||
* Returns a context object for a given block. | ||
* | ||
* @param {string} clientId The block client ID. | ||
* | ||
* @return {Record<string,*>} Context value. | ||
*/ | ||
export default function useBlockContext( clientId ) { | ||
return useSelect( | ||
( select ) => { | ||
const block = select( blockEditorStore ).getBlock( clientId ); | ||
if ( ! block ) { | ||
return undefined; | ||
} | ||
|
||
const blockType = select( blocksStore ).getBlockType( block.name ); | ||
if ( ! blockType ) { | ||
return undefined; | ||
} | ||
|
||
if ( Object.keys( blockType.providesContext ).length === 0 ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any chance There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When There's only one edge case: if I specify There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's worth adding a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realized there's the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds like intentionally breaking a block. What if someone sets a string instead of an object? I think this is something that block register should handle. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. String is fine, it's an array-like object that has keys. Even number will be accepted. Only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't disagree, after all, we probably have many places where we expect a string value in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. That's why I approved the PR in the first place and considered this a minor thing. Leaving it to your judgement @Mamaduka. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks again for the great feedback. I will merge this as it is. But happy to add extra checks in the future. Maybe as a part of block registration. |
||
return undefined; | ||
} | ||
|
||
return Object.fromEntries( | ||
Object.entries( blockType.providesContext ).map( | ||
( [ contextName, attributeName ] ) => [ | ||
contextName, | ||
block.attributes[ attributeName ], | ||
] | ||
) | ||
); | ||
}, | ||
[ clientId ] | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This original condition was always true, so there's no harm in wrapping the block in a context provider similar to the web version. See #46729.