WordPress のブロックエディタ(Gutenberg)用のブロック開発にて、親ブロックの属性(attributes)の値の変更を子ブロックである InnerBlocks 側で動的に検知し、その値(条件)に応じて InnerBlocks 側で処理を分岐したり InnerBlocks の属性を更新したい場合があります。
例えば、ボタングループ用の親ブロックで文字色やボタン背景色などの設定を行い、その情報を InnerBlocks (各ボタンブロック)に伝えたい場合など。
よくある方法(めんどくさい)
そのようなとき、よくある方法としては インナーブロック用の子ブロックにも同じ属性を定義しておき、以下のように親ブロックの edit.js で自身に含まれるインナーブロックを取得し、updateBlockAttributes 関数で子ブロックの属性を更新するというもの。
import { InspectorControls, useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
import { dispatch, select } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
// 許可するインナーブロック
const ALLOWED_BLOCKS = [
'my-plugin/button',
]
// インナーブロックのテンプレート
const TEMPLATE = [
[ 'my-plugin/button' ],
]
export const Edit = props => {
const {
attributes: {
buttonColor,
buttonColorSlug
},
clientId
} = props;
:
const { getBlocksByClientId } = select( 'core/block-editor' );
const { updateBlockAttributes } = dispatch( 'core/block-editor' );
const thisBlock = getBlocksByClientId( clientId );
useEffect( () => {
if ( thisBlock && thisBlock[0] ) {
// インナーブロックをすべて取得
const thisInnerBlocks = thisBlock[0]?.innerBlocks
if ( Array.isArray( thisInnerBlocks ) ) {
// 取得したインナーブロックごとに処理
thisInnerBlocks.map( innerBlock => {
// 現在のインナーブロックの attributes を更新
updateBlockAttributes( innerBlock.clientId, {
buttonColorSlug: buttonColorSlug,
} );
} );
}
}
}, [ thisBlock, buttonColorSlug ] );
:
const blockProps = useBlockProps();
// サイドバーコントロール
const sideBar = (
<InspectorControls>
:
</InspectorControls>
);
// インナーブロック
const innerBlocksProps = useInnerBlocksProps( blockProps, {
allowedBlocks: ALLOWED_BLOCKS,
template: TEMPLATE ,
templateInsertUpdatesSelection: true,
} );
return (
{ sideBar }
<div { ...innerBlocksProps } />;
)
}
export default Edit
この方法でも実現できますが、この場合は子ブロックに専用の属性を与えて置く必要があることと、あちこちから関数を呼び出してインナーブロックごとにループ処理させているため、コードが長くなりわかりにくいです。
親の変更を子に伝える簡単な方法
親ブロックの属性の変更を子ブロックに伝える(受け取る)には、もっとシンプルなやり方があります。
その方法は、親ブロックの block.json に "providesContext" パラメータを追加し、子ブロックに渡したい値を、名前空間を付けたコンテキストキーとそれに対応する親ブロックの属性名のオブジェクトで指定し、子ブロックの block.json には "usesContext" パラメータの値に、親ブロックで名付けたコンテキストキーをセットしておくという方法です。
実際にコードを見てみましょう。
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "my-plugin/buttons",
"title": "Button Group",
"providesContext": {
"my-plugin/button_color_slug": "buttonColorSlug"
},
"attributes": {
"buttonColorSlug": {
"type": "string",
"default": ""
}
},
:
}
この例では、buttonColorSlug という親ブロックの属性を my-plugin/button_color_slug という名前空間(my-plugin/)付きのコンテキストキーに割り当てています。
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "my-plugin/button",
"title": "Button",
"usesContext": [ "my-plugin/button_color_slug" ],
"attributes": {
"buttonColorSlug": {
"type": "string",
"default": ""
}
},
:
}
子ブロックの block.json では、usesContext で親ブロックで定義したコンテキストキー(my-plugin/button_color_slug)を利用するように指示するだけです。
そして、子ブロックの edit.js では、以下のようにして親ブロックのコンテキストキーの値の変更を検知し、処理を分岐したり子ブロックの属性を更新することができるようになります。
// 親ブロックの値を取得
const prentButtonColorSlug = props.context[ 'my-plugin/button_color_slug' ];
// 親ブロックの値が変更されたら子ブロックの値も更新
useEffect( () => {
setAttributes( { buttonColorSlug: prentButtonColorSlug } );
}, [ prentButtonColorSlug ] );
このようにして親ブロックの属性値の変更を子ブロック側で受け取ることができるので、最初の updateBlockAttributes を利用したやり方に比べてかなりコードがスッキリして分かりやすくなります。
また、ブロックをサーバーサイドでレンダリングする動的ブロック(PHP)でもコンテキストを利用できます。
