前回は高階コンポーネントについて解説しました。
WordPressのブロック開発メモ その7 HOC(高階コンポーネント)
今回はフックについてです。
ブロックにもワードプレス本体のフックのようなものがあります。
これは特に説明するような内容では無いのでドキュメント読んでください。
フックはイベントのような物で、何かの処理が行われたさいに処理するコールバックを登録します。
フックはフィルターとアクションがあります。
アクションとフィルターの違いをラムダ式、アロー関数で表現すると、
アクション
(data) => 何かする
フィルター
(data) => dataを編集してdataを返す
の違いがあります。フィルターは戻り値を返します。
全てを把握するのは大変なのでいくつか触ってみます。
フックをindex.ts
に追加します。
Block側のフック
blocks.registerBlockType
import { addFilter } from '@wordpress/hooks';
addFilter(
'blocks.registerBlockType',
'kurage-hooks/test-1',
(settings, name: string) =>
{
if(name === 'create-block/kurage-worker')
{
console.log(settings);
}
return settings;
}
);
コンソールを見ると見おぼえあるプロパティが!
block.jsonの設定を使用する前に上書することが出来ます。
フィルタを登録するにはaddFilter()
を使います。
第一引数にフック名を指定します。
設定情報を上書するにはblocks.registerBlockType
を指定します。
このフィルタはregisterBlockType()
でブロックの追加したときに呼び出されます。
第二引数はフックの名前空間(ご自由に)。
第三引数でコールバックを指定します。
コールバックから渡される引数はフック毎に異なります。
今回はsettings
で設定の内容を、name
はブロックの名前が渡されます。
注意しないといけないのは、登録されているコアのブロックも受け取ってしまうことです。
結構な量のコアブロックが登録してあるので、ブロックの名前(name
)で目的のブロックを判断します。
settings
を上書する必要があれば上書します。
必ず戻り値を返します。
blocks.getSaveElement
save
を上書します。
index.tsx
addFilter(
'blocks.getSaveElement',
'create-block/kurage-save',
(element, blockType, attributes) =>
{
if(blockType.name === 'create-block/kurage-worker')
{
if(element)
{
return (<div className="kurage-wrap">{element}</div>)
}
}
return element;
}
)
save.tsx
export default (props: BlockSaveProps<KurageExampleBlockProps>) =>
{
return (
<p { ...useBlockProps.save() }>
Hello Kurage
</p>
);
}
style.scss
.kurage-wrap
{
border: 3px dotted green;
padding: 1rem;
}
編集ページの画面
プレビューでの表示
フィルタの適用をKurage Worker
ブロックに限定してます。
if(blockType.name === 'create-block/kurage-worker')
動作を確認するため編集ページでKurage Worker
ブロック意外にカスタムHTML
ブロックと画像
ブロックも追加します。
プレビューを開くとKurage Worker
ブロックだけに限定されているのが確認出来ます。
もちろんifを使わず全てのブロックに適用することも出来ます。
保存するとカスタムHTML
ブロックや画像
ブロックにまで適用されてます。
editor.BlockEdit と editor.BlockListBlock
BlockListBlock
とBlockEdit
をフィルタリングします。
このBlockListBlock
、ちょーがつくほど重要で、後で深堀していきます。
まずは次の例を見てみましょう。
style.scss
.kurage-block
{
border: 8px dotted green;
padding: 1rem;
margin: 5rem;
}
.kurage-list
{
border: 8px dotted red;
padding: 1rem;
}
.kurage-content
{
border: 8px dotted rgb(255, 174, 0);
padding: 1rem;
}
index.tsx
addFilter(
'editor.BlockEdit',
'create-block/kurage-be',
createHigherOrderComponent((BlockEdit) =>
{
return props => (
<div className="kurage-block">
<p>BlockEdit Begin</p>
<BlockEdit {...props} />
<p>BlockEdit End</p>
</div>
)
}, 'MyBlockEdit')
)
addFilter(
'editor.BlockListBlock',
'create-block/kurage-blb',
createHigherOrderComponent((BlockListBlock) =>
{
return props => (
<>
<div className='kurage-list'>
<p>BlockListBlock Begin</p>
<BlockListBlock {...props} className="kurage-content" />
<p>BlockListBlock End</p>
</div>
</>
)
}, 'MyBlockListBlock')
);
何故かBlockListBlock
で指定したclassName
はedit
側に適用されます。
何故かというか、そういう設計がされてます。
フィルタから受け取る引数のBlockListBlock
がブロック本体で、BlockEdit
が編集コンポーネント(edit
)です。
wrapperPropsを使うと
filter
側
addFilter(
'editor.BlockEdit',
'create-block/kurage-be',
createHigherOrderComponent((BlockEdit) =>
{
return props => (
<div className="kurage-block">
<p>BlockEdit Begin</p>
<BlockEdit {...props} />
<p>BlockEdit End</p>
</div>
)
}, 'MyBlockEdit')
)
addFilter(
'editor.BlockListBlock',
'create-block/kurage-blb',
createHigherOrderComponent((BlockListBlock) =>
{
return props => (
<>
<div className='kurage-list'>
<p>BlockListBlock Begin</p>
<BlockListBlock {...props} className="kurage-content" wrapperProps={{'data-message': 'Kurage Attack!'}} />
<p>BlockListBlock End</p>
</div>
</>
)
}, 'MyBlockListBlock')
);
BlockListBlock
のwrapperProps
でdata-message
プロパティを追加。
edit
側
export default (props: BlockEditProps<KurageExampleBlockProps>) =>
{
const cc = useBlockProps();
return (
<div {...useBlockProps()}>
<div style={{whiteSpace: 'pre'}}>
{ JSON.stringify(cc, null, "\t") }
</div>
Hello Kurage
</div>
);
}
wrapperProps
で設定したプロパティは編集コンポーネント側のuseBlockProps()
で取得出来ます。
data-message
がKurage Attack!
になっていることが確認出来ます。
またDOMにも反映していることが確認出来ます。
これまで何となく使ってきたuseBlockProps()
は実はBlockListBlock
で設定されたコンテクストでもあります(後で説明)。
フックの追加時、削除時に呼び出されるアクション
addAction()を使い、フックの追加と削除を知ることが出来ます。
ただし、呼び出す順番によって内容が変わるので注意してください。
import { addAction, addFilter, removeFilter } from '@wordpress/hooks';
addAction('hookRemoved', 'kurage/hookRemoved', fn => {
console.log(` > ${fn} が削除されました`);
});
addAction('hookAdded', 'kurage/hookAdded', fn => {
console.log(` > ${fn} が追加されました`);
});
addFilter('MyHooks', 'kurage/my-hooks', v => {});
removeFilter('MyHooks', 'kurage/my-hooks');
MyHooks が追加されました
MyHooks が削除されました
editor.BlockEdit が追加されました
editor.BlockListBlock が追加されました
blocks.switchToBlockType.transformedBlock が追加されました
blocks.switchToBlockType.transformedBlock が追加されました
blocks.registerBlockType が追加されました
editor.Autocomplete.completers が追加されました
editor.MediaUpload が追加されました
editor.BlockEdit が追加されました
blocks.registerBlockType が追加されました
blocks.registerBlockType が追加されました
editor.BlockEdit が追加されました
blocks.registerBlockType が追加されました
blockEditor.__unstableCanInsertBlockType が追加されました
blocks.registerBlockType が追加されました
blockEditor.__unstableCanInsertBlockType が追加されました
plugins.pluginRegistered が追加されました
plugins.pluginUnregistered が追加されました
heartbeat.send が追加されました
heartbeat.tick が追加されました
コアで登録されたフックもまとめて検出されてます。
ただしhookAdded
を登録する前のフックの追加は検出出来ていません。
ログのうち今回登録/削除したMyHooks
の部分を見るとちゃんと機能していることが確認できます。
MyHooks が追加されました
MyHooks が削除されました
きちんとフックを検出出来てます。
アクションとフィルタ
さて、アクション、フィルタ、これらはPHP側にもあるのでわざわざ説明しなくてもいいと思いますが。
一応試してみましょう。
アクション
アクションはイベントのような物です。
import { addAction, addFilter, doAction } from '@wordpress/hooks';
addAction('Fired', 'kurage/fire-1', (...args) => console.log(`fire-1: ${args.join(', ')}`))
addAction('Fired', 'kurage/fire-2', (...args) => console.log(`fire-2: ${args.join(', ')}`))
doAction('Fired', 'Hello', 'World');
fire-1: Hello, World
fire-2: Hello, World
addAction()
によってFired
アクションを二つ登録しています。
どちらも引数をまとめてコンソールに出力するものです。
applyFilter()
でアクションを実行します。
Fired
フック名で登録されたコールバックを呼び出します。
その際、Hello
とWorld
二つの引数を渡してます。
フィルタ
フィルタはcompose()のようなものです。
import { addFilter, applyFilters } from '@wordpress/hooks';
addFilter('MyValue', 'kurage/plusplus', value => value + 1);
addFilter('MyValue', 'kurage/pow', value => value * value);
const val = applyFilters('MyValue', 3);
console.log(val);
16
最初に登録したフィルタはkurage/plusplus
で値を1足して返すフィルタです。
次に登録したフィルタはkurage/pow
で値を二乗にして返すフィルタです。
フィルタは値を必ず返します。
applyFilter()
でMyValue
の名前で登録されたフィルタに3
を渡してます。
3に1足して二乗した値をapplyFilter()
は戻り値として返します。
3に1を足して4、さらに二乗した16が戻り値です。
PHP側のフック
ついでにPHP側のフィルタの一部を見てみます。
block_type_metadata
以前kurage-worker.php
でブロックを登録しました。
register_block_type( __DIR__ . '/build' );
このブロック登録の関数は引数がブロックのJSONファイルであればそれを読み込みます。
その読み込んだデータに変更を加えることが出来ます。
add_filter('block_type_metadata', function($metadata){
if($metadata['name'] === 'create-block/kurage-worker')
{
sleep(1);
}
return $metadata;
});
ブレークポイントで止めています。
コアのブロック含め片っ端から検出してしまうので自分で作ったブロックの名前の時だけスリープするようにしてます。
block_type_metadata_settings
ちょっと違いが分かりにくいが、register_block_type()
って次のような使い方が出来る。
register_block_type(
'kurage/example-block',
[
'editor_script' => 'my-block',
'style' => 'my-block-style',
'editor_style' => 'my-block-editor-style'
]
);
ブロックを登録する関数で、引数にそのブロックのデータを渡すのだけど、
第一引数がJOSN
だった場合はそのデータが読み込まれ、第二引数とマージされる。
このJSON
だった時、JSONのプロパティ名とPHPのプロパティ名では命名規則が異なったり差異が存在する。
そこでJSONから読み込まれたデータはPHPの命名規則に変換する処理が行われる。
もちろんそれ以外にもいろいろな事情で変換処理が行われるんだろうけども。
最終的なPHP命名規則のデータはWP_Block_Type
のコンストラクタに渡される。
試しに$settings
, $metadata
のキーにアンダースコアか大文字が入っている名前を列挙してみた。
$aaaaa = [];
add_filter('block_type_metadata_settings', function($settings, $metadata){
static $s = [];
static $m = [];
$skeys = array_keys($settings);
$mkeys = array_keys($metadata);
$check = function($key)
{
return strpos($key, '_') !== false || preg_match('/[A-Z]/', $key);
};
$s = array_unique(array_merge($s, array_filter($skeys, fn($key) => $check($key))));
$m = array_unique(array_merge($m, array_filter($mkeys, fn($key) => $check($key))));
global $aaaaa;
$aaaaa = [$s, $m];
return $metadata;
}, 10, 2);
add_action('wp_loaded', function(){
global $aaaaa;
$x = 4;
});
BlockListBlockを深堀していく。
ここからはかなり深い内容です。
普通にブロック開発するうえで基本的に必要ありません。
どうしてもBlockListBlock
の正体が気になる人だけお付き合いいただければ。
ブロックの階層を知る
editor.BlockListBlock
とeditor.BlockEdit
はどう違う?
これを理解するためにグーテンベルグのコードを追っていきました。
- 実際に試したわけではなくコードを目で追っただけなので間違っている可能性があります。
編集ページの階層はどうなってるのか気になって調べてみました。
実際にはコンテクスト類がごちゃごちゃ入ってますが大雑把に省いたらこんな感じになってました。
<App>
<Layout>
<Editor>
<BlockEditor>
<BlockList>
<BlockListBlock>
<BlockEdit>
<Edit />
</BlockEdit>
</BlockListBlock>
</BlockList>
</BlockEditor>
</Editor>
</Layout>
</App>
この階層を把握するのはとても重要です。
editor.BlockListBlock
はBlockListBlock
で、
editor.BlockEdit
はEdit(BlockEditではない)
でフィルタが使用されます。
それぞれのコンポーネントのソースコードです。
この記事を書いている時点のもので「行」などの変更がある可能性もあります。
その点には注意してください。
@wordpress/edit-site
initializeEditor()
https://github.dev/WordPress/gutenberg/blob/trunk/packages/edit-site/src/index.js#L92
App
https://github.dev/WordPress/gutenberg/blob/trunk/packages/edit-site/src/components/app/index.js#L39
Layout
Editor
BlockEditor
@wordpress/block-editor
// BlockListBlockはBlockListItemsコンポーネントを呼び出すことによってレンダリングされるが、そのコンポーネントはuseInnerBlockProps()から取得される。
BlockList
useInnerBlocksProps()
BlockListBlock
BlockEdit
Edit
BlockListBlock
がブロックの本体のような感じで、
例えば検証エラーの時はそれを表示したり、HTMLとして編集できるようにしたり、配置(data-align)を実装したり一通りの実装をしている。
まだ紹介してないがwithSelect()
やwithDispatch()
が適用されているところでもある。
ソースコードを見ると次のように定義してある。
// BlockListBlockの本体はめっちゃ長いコードなので省略。
function BlockListBlock(...) ...
// compose()でeditor.BlockListBlockが適用されている
export default compose(
pure,
applyWithSelect,
applyWithDispatch,
// Block is sometimes not mounted at the right time, causing it be undefined
// see issue for more info
// https://github.com/WordPress/gutenberg/issues/17013
ifCondition( ( { block } ) => !! block ),
withFilters( 'editor.BlockListBlock' )
)( BlockListBlock );
次にEdit
。
ブロック登録の際の設定情報のsave
やedit
をレンダリングする場所でもある。
Edit
が行ってることをいろいろ省いて大雑把に書いてみるとこんな感じになる。
// 本体のソースコードが若干複雑なので簡素化してます。
export const Edit = props => {
// registerBlockType(name, ...)で設定した情報を取得
const blockType = getBlockType(props.name);
// edit or save を取得
const Component = blockType.edit || blockType.save;
// edit or save をレンダリング
return <Component ... />
}
// compose()でeditor.BlockEditが適用されている
export default withFilters( 'editor.BlockEdit' )( Edit );
今までregisterBlockType()
で渡す設定データのedit
やsave
などのコンポーネントが使用される決定的瞬間!
editor.BlockEdit
もここで使用される。
さてここで出てきたwithFilters()
ですが、
withFilters()
はaddFilter()/removeFilter()
によってeditor.BlockEdit
フックに変更があった場合に更新出来るように工夫されたものです。
withFilters()
withFilters()のソースコード
withFilters()のHOCがどういうものかというと、
OriginalComponent
を受け取ってFilteredComponentRenderer
を返す高階関数です。
間にapplyFilter()
でフィルタリングしたFilteredComponent
の三つが存在します。
それぞれの役割は
コンポーネント名 | 役割 |
---|---|
OriginalComponent | 引数で受け取った元のコンポーネント |
FilteredComponent | applyFilter()でフィルタリングした後のコンポーネント |
FilteredComponentRenderer | FilteredComponentをレンダリングする用のコンポーネント |
です。
このうち引数で受け取るOriginalComponent
と、戻り値のFilteredComponentRenderer
は不変であり、FilteredComponent
は可変です。
まずフックは追加したり削除したりすることが出来ることを考えないといけません。
フックの変更があるとそのフィルタリングが返す結果は異なる可能性があります。
そのためフックの変更毎にフィルタリング結果(FilteredComponent
)を更新する必要があります。
そこで元のコンポーネント
、フィルタリングされた後のコンポーネント
、フィルタリングされたコンポーネントをレンダリングするコンポーネント
の三段構えになってます。
ではフックの変更をどう管理しているのでしょう?
FilteredComponentRenderer
はマウント/アンマウントを監視してます。
そしてインスタンスのコレクションを持っています。
マウントされたらそのインスタンスを追加し、アンマウントされたら削除します。
const FilteredComponentRenderer = withFilters(...)(OriginalComponent);
// 一つ目
<FilteredComponentRenderer />
// 二つ目
<FilteredComponentRenderer />
// 三つ目
<FilteredComponentRenderer />
もし一つ目のコンポーネントがマウントされた時、addAction('hookRemove')
及びaddAction('hookAdded')
のアクションを登録します。
これらはフックの削除時、追加時に呼ばれるアクションです。
もしwithFilters(フック名)
で指定したフック名を受け取った時、
- 再フィルタリングして
FilteredComponent
を更新する - 監視しているインスタンスをまとめて更新する(
forceUpdate()
)
をします。
つまり、
- フィルタの更新を察知
- 再フィルタリングで
FilteredComponent
を更新 - コレクションされている
FilteredComponentRenderer
一覧を再レンダリング
みたいな感じになってます。
アクションとフィルタの一覧を取得
以下のコードはshow()
関数を実行するとその時点での登録されたフック名(そのフックを登録した名前空間のリスト)を表示するものです。
特に理由はないですがそれぞれ3つのタイミングで表示してます。
import { actions, addAction, addFilter, filters } from '@wordpress/hooks';
// filters, actions をまとめて表示
function show(nbr: number)
{
// @ts-ignore
const actionKeys = Object.entries(actions).map(([k, v]) => `${k} (${v?.handlers?.map(_=>_.namespace).join(', ')})`);
// @ts-ignore
const filterKeys = Object.entries(filters).map(([k, v]) => `${k} (${v?.handlers?.map(_=>_.namespace).join(', ')})`);
console.log(`-------------- Show(${nbr}) ----------------`);
console.log(`actions\n ${actionKeys.join("\n")}`);
console.log(`filters\n ${filterKeys.join("\n")}`);
}
show(1);
addAction('hookAdded', 'kurage/hookAdded', fn => {});
show(2);
setTimeout(() => show(3), 2000);
actions
から登録されたアクション一覧を取得します。
filters
から登録されたフィルター一覧を取得します。
show()はそれらの一覧を表示するもので3回呼び出してます。
結果
-------------- Show(1) ----------------
actions
__current (undefined)
hookAdded (core/i18n)
hookRemoved (core/i18n)
filters
__current (undefined)
i18n.gettext ()
i18n.gettext_default ()
i18n.gettext_with_context ()
i18n.gettext_with_context_default ()
blocks.registerBlockType (core/compat/migrateLightBlockWrapper, core/align/addAttribute, core/lock/addAttribute, core/anchor/attribute, core/ariaLabel/attribute, core/custom-class-name/attribute, core/border/addAttributes, core/border/addEditProps, core/color/addAttribute, core/color/addEditProps, core/fontFamily/addAttribute, core/fontFamily/addEditProps, core/font/addAttribute, core/font/addEditProps, core/style/addAttribute, core/style/addEditProps, core/settings/addAttribute, core/editor/duotone/add-attributes, core/layout/addAttribute, core/metadata/addMetaAttribute, core/metadata/addLabelCallback, core/font-size/addEditPropsForFluidCustomFontSizes)
editor.BlockListBlock (core/editor/align/with-data-align, core/border/with-border-color-palette-styles, core/color/with-color-palette-styles, core/font-size/with-font-size-inline-styles, core/editor/with-elements-styles, core/editor/duotone/with-styles, core/editor/layout/with-layout-styles)
editor.BlockEdit (core/editor/align/with-toolbar-controls, core/editor/anchor/with-inspector-control, core/editor/custom-class-name/with-inspector-control, core/style/with-block-controls, core/editor/duotone/with-editor-controls, core/editor/layout/with-inspector-controls, core/style/with-block-controls)
blocks.getSaveContent.extraProps (core/align/addAssignedAlign, core/anchor/save-props, core/ariaLabel/save-props, core/custom-class-name/save-props, core/generated-class-name/save-props, core/border/addSaveProps, core/color/addSaveProps, core/fontFamily/addSaveProps, core/font/addSaveProps, core/style/addSaveProps, core/metadata/save-props)
blocks.switchToBlockType.transformedBlock (core/color/addTransforms, core/color/addTransforms, core/font-size/addTransforms)
-------------- Show(2) ----------------
actions
__current (undefined)
hookAdded (core/i18n, kurage/hookAdded)
hookRemoved (core/i18n)
filters
__current (undefined)
i18n.gettext ()
i18n.gettext_default ()
i18n.gettext_with_context ()
i18n.gettext_with_context_default ()
blocks.registerBlockType (core/compat/migrateLightBlockWrapper, core/align/addAttribute, core/lock/addAttribute, core/anchor/attribute, core/ariaLabel/attribute, core/custom-class-name/attribute, core/border/addAttributes, core/border/addEditProps, core/color/addAttribute, core/color/addEditProps, core/fontFamily/addAttribute, core/fontFamily/addEditProps, core/font/addAttribute, core/font/addEditProps, core/style/addAttribute, core/style/addEditProps, core/settings/addAttribute, core/editor/duotone/add-attributes, core/layout/addAttribute, core/metadata/addMetaAttribute, core/metadata/addLabelCallback, core/font-size/addEditPropsForFluidCustomFontSizes)
editor.BlockListBlock (core/editor/align/with-data-align, core/border/with-border-color-palette-styles, core/color/with-color-palette-styles, core/font-size/with-font-size-inline-styles, core/editor/with-elements-styles, core/editor/duotone/with-styles, core/editor/layout/with-layout-styles)
editor.BlockEdit (core/editor/align/with-toolbar-controls, core/editor/anchor/with-inspector-control, core/editor/custom-class-name/with-inspector-control, core/style/with-block-controls, core/editor/duotone/with-editor-controls, core/editor/layout/with-inspector-controls, core/style/with-block-controls)
blocks.getSaveContent.extraProps (core/align/addAssignedAlign, core/anchor/save-props, core/ariaLabel/save-props, core/custom-class-name/save-props, core/generated-class-name/save-props, core/border/addSaveProps, core/color/addSaveProps, core/fontFamily/addSaveProps, core/font/addSaveProps, core/style/addSaveProps, core/metadata/save-props)
blocks.switchToBlockType.transformedBlock (core/color/addTransforms, core/color/addTransforms, core/font-size/addTransforms)
react.min.js?ver=17.0.1:9 [Violation] 'message' handler took 186ms
-------------- Show(3) ----------------
actions
__current (undefined)
hookAdded (core/i18n, kurage/hookAdded, core/with-filters/editor.BlockEdit, core/with-filters/editor.BlockListBlock)
hookRemoved (core/i18n, core/with-filters/editor.BlockEdit, core/with-filters/editor.BlockListBlock)
plugins.pluginRegistered (core/plugins/plugin-area/plugins-registered)
plugins.pluginUnregistered (core/plugins/plugin-area/plugins-unregistered)
heartbeat.send (core/editor/post-locked-modal-0)
heartbeat.tick (core/editor/post-locked-modal-0)
filters
__current (undefined)
i18n.gettext ()
i18n.gettext_default ()
i18n.gettext_with_context ()
i18n.gettext_with_context_default ()
blocks.registerBlockType (core/compat/migrateLightBlockWrapper, core/align/addAttribute, core/lock/addAttribute, core/anchor/attribute, core/ariaLabel/attribute, core/custom-class-name/attribute, core/border/addAttributes, core/border/addEditProps, core/color/addAttribute, core/color/addEditProps, core/fontFamily/addAttribute, core/fontFamily/addEditProps, core/font/addAttribute, core/font/addEditProps, core/style/addAttribute, core/style/addEditProps, core/settings/addAttribute, core/editor/duotone/add-attributes, core/layout/addAttribute, core/metadata/addMetaAttribute, core/metadata/addLabelCallback, core/editor/custom-sources-backwards-compatibility/shim-attribute-source, block-directory/fallback, core/navigation-link, core/template-part, core/template-part, core/font-size/addEditPropsForFluidCustomFontSizes)
editor.BlockListBlock (core/editor/align/with-data-align, core/border/with-border-color-palette-styles, core/color/with-color-palette-styles, core/font-size/with-font-size-inline-styles, core/editor/with-elements-styles, core/editor/duotone/with-styles, core/editor/layout/with-layout-styles, create-block/kurage-blb)
editor.BlockEdit (core/editor/align/with-toolbar-controls, core/editor/anchor/with-inspector-control, core/editor/custom-class-name/with-inspector-control, core/style/with-block-controls, core/editor/duotone/with-editor-controls, core/editor/layout/with-inspector-controls, core/style/with-block-controls, create-block/kurage-be, core/edit-post/validate-multiple-use/with-multiple-validation, core/query)
blocks.getSaveContent.extraProps (core/align/addAssignedAlign, core/anchor/save-props, core/ariaLabel/save-props, core/custom-class-name/save-props, core/generated-class-name/save-props, core/border/addSaveProps, core/color/addSaveProps, core/fontFamily/addSaveProps, core/font/addSaveProps, core/style/addSaveProps, core/metadata/save-props)
blocks.switchToBlockType.transformedBlock (core/color/addTransforms, core/color/addTransforms, core/font-size/addTransforms, core/gallery/update-third-party-transform-to, core/gallery/update-third-party-transform-from)
editor.Autocomplete.completers (editor/autocompleters/set-default-completers)
editor.MediaUpload (core/edit-post/replace-media-upload)
plugins.registerPlugin ()
blockEditor.__unstableCanInsertBlockType (removeTemplatePartsFromPostTemplates, removeTemplatePartsFromInserter)
blocks.getBlockAttributes ()
blocks.getBlockDefaultClassName ()
blocks.getSaveElement ()
この結果の中でhookAdded
のみに注目します。
最初のshow(1)
hookAdded (core/i18n)
2回目はaddAction('hookAdded', 'kurage/hookAdded', fn => {});
を呼び出した直後
hookAdded (core/i18n, kurage/hookAdded)
3回目はsetTimeout(() => show(3), 2000);
を呼び出した後
hookAdded (core/i18n, kurage/hookAdded, core/with-filters/editor.BlockEdit, core/with-filters/editor.BlockListBlock)
withFilters()
がhookAdded
フックを登録した時の名前がcore/with-filters/editor.BlockEdit
とcore/with-filters/editor.BlockListBlock
だと分かります。
BlockListBlockとuseBlockProps()の関係
ドキュメントにwrapperProps
の詳しい解説が無かったので追って見ました。
BlockListBlock
のwrapperProps
は他のいくつかのプロパティと共にmemoizedValue
にメモ化され、
レンダリングでBlockListBlockContext
に渡されます。
<BlockListBlockContext.Provider value={ memoizedValue }>
<BlockCrashBoundary
fallback={
<Block className="has-warning">
<BlockCrashWarning />
</Block>
}
>
{ block }
</BlockCrashBoundary>
</BlockListBlockContext.Provider>
上記の block
の部分が最終的に編集コンポーネントをレンダリングします。
useBlockContext()
の定義を見てみます。
ここではBlockListBlockContext
のコンテクストが取得されます。
取得されたwrapperProps
等は展開され戻り値として返されます。
export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
const {
clientId,
className,
wrapperProps = {},
isAligned,
} = useContext( BlockListBlockContext );
// 省略
return ({
...wrapperProps,
// 省略(他にもセレクタ等からいっぱい展開される)
})
その値は編集コンポーネントで使用できます。
export default (props: BlockEditProps<KurageExampleBlockProps>) =>
{
const cc = useBlockProps();
return (
<div {...cc}>
<div style={{whiteSpace: 'pre'}}>
{ JSON.stringify(cc, null, "\t") }
</div>
Hello Kurage
</div>
);
}