2026年9月15日

2026年9月15日

WordPressのブロック開発でHigher-Order Component(HOC)を使う方法

WordPress のブロックエディターで Higher-Order Component(HOC、高階コンポーネント)を使う代表的な場面は、editor.BlockEdit などのフィルターでコアブロックの編集画面を包み、設定項目を追加するときです。HOC は @wordpress/compose の createHigherOrderComponent で作り、@wordpress/hooks の addFilter で登録します。この記事では、HOC の考え方、段落ブロックに独自のトグルを追加する実例、公開画面への反映、つまずきやすい点と確認方法を説明します。

HOCとは何か、WordPressのどこで使われているか

HOC は「コンポーネントを受け取り、機能を足した新しいコンポーネントを返す関数」です。元のコンポーネントを書き換えずに、表示の前後に要素を足したり、渡す props を増やしたりできます。React の一般的な設計手法の一つで、WordPress のブロックエディターも内部でこの形を多用しています。

WordPress で HOC に触れるのは、主に次の場面です。

  • ブロックフィルター: editor.BlockEdit や editor.BlockListBlock は、ブロックの編集用コンポーネントを受け取って包む HOC を登録する仕組みです。コアブロックや他のプラグインのブロックの編集画面に手を加えるときの、標準的な方法です。
  • データの取得: @wordpress/data の withSelect・withDispatch は、ストアのデータや操作を props として渡す HOC です。
  • 複数の HOC の合成: @wordpress/compose の compose で、複数の HOC を1つにまとめられます。

ただし、データの取得については現在は useSelect・useDispatch といったフックで書くのが一般的です(2026年10月現在)。新しく書くコードで HOC が必要になるのは、ほとんどがブロックフィルターの場面です。

開発環境を用意する

ここではプラグインとして作ります。JSX を使うため、@wordpress/scripts でビルドします。

mkdir -p wp-content/plugins/my-highlight/src
cd wp-content/plugins/my-highlight
npm init -y
npm install --save-dev @wordpress/scripts

package.json の scripts に "build": "wp-scripts build" と "start": "wp-scripts start" を追加すると、src/index.js が build/index.js にビルドされ、依存スクリプトの一覧を書いた build/index.asset.php も生成されます。@wordpress/compose などのパッケージは、WordPress 本体が提供するスクリプトとして読み込まれるため、個別にバンドルする必要はありません。

段落ブロックに設定項目を追加するHOCを作る

例として、段落ブロックに「強調表示」のトグルを追加します。手順は、属性を追加する、編集画面に入力欄を出す HOC を作る、の2つです。

import { addFilter } from '@wordpress/hooks';
import { createHigherOrderComponent } from '@wordpress/compose';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, ToggleControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

const TARGET = 'core/paragraph';

// 1. 段落ブロックに属性 isHighlighted を追加する
addFilter(
	'blocks.registerBlockType',
	'my-highlight/add-attribute',
	( settings, name ) => {
		if ( name !== TARGET ) {
			return settings;
		}
		return {
			...settings,
			attributes: {
				...settings.attributes,
				isHighlighted: { type: 'boolean', default: false },
			},
		};
	}
);

// 2. 編集画面の BlockEdit を包み、サイドバーにトグルを追加する HOC
const withHighlightControl = createHigherOrderComponent( ( BlockEdit ) => {
	return ( props ) => {
		if ( props.name !== TARGET ) {
			return <BlockEdit { ...props } />;
		}
		const { attributes, setAttributes } = props;
		return (
			<>
				<BlockEdit { ...props } />
				<InspectorControls>
					<PanelBody title={ __( '強調表示', 'my-highlight' ) }>
						<ToggleControl
							label={ __( '背景色を付けて強調する', 'my-highlight' ) }
							checked={ !! attributes.isHighlighted }
							onChange={ ( value ) =>
								setAttributes( { isHighlighted: value } )
							}
						/>
					</PanelBody>
				</InspectorControls>
			</>
		);
	};
}, 'withHighlightControl' );

addFilter(
	'editor.BlockEdit',
	'my-highlight/with-highlight-control',
	withHighlightControl
);

createHigherOrderComponent の第2引数は表示名です。React の開発者ツールでコンポーネントが withHighlightControl(...) のような名前で表示されるので、どの HOC が包んでいるかを追いやすくなります。

HOC の中では、対象外のブロックに対して <BlockEdit { ...props } /> をそのまま返すことが大切です。editor.BlockEdit は全ブロックに適用されるため、ここで props を渡し忘れたり何も返さなかったりすると、すべてのブロックの編集画面が壊れます。対象の判定は関数の先頭で行い、対象外のときは余計な処理をせずに返すと、ブロック数の多い投稿でも重くなりにくくなります。

編集画面のブロックにクラスを付ける

トグルを入れても、このままでは見た目が変わりません。編集画面でブロックにクラスを付けるには、editor.BlockListBlock に別の HOC を登録します。

const withHighlightClass = createHigherOrderComponent( ( BlockListBlock ) => {
	return ( props ) => {
		if ( props.name !== TARGET || ! props.attributes.isHighlighted ) {
			return <BlockListBlock { ...props } />;
		}
		const className = [ props.className, 'is-highlighted' ]
			.filter( Boolean )
			.join( ' ' );
		return <BlockListBlock { ...props } className={ className } />;
	};
}, 'withHighlightClass' );

addFilter(
	'editor.BlockListBlock',
	'my-highlight/with-highlight-class',
	withHighlightClass
);

editor.BlockListBlock で付けたクラスは編集画面だけのもので、保存される HTML には含まれません。公開画面への反映は次のように PHP 側で行います。

スクリプトの読み込みと公開画面への反映

プラグイン本体の PHP では、エディター用スクリプトの読み込みと、公開画面でのクラス付与を行います。

<?php
/**
 * Plugin Name: My Highlight
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_action( 'enqueue_block_editor_assets', function () {
	$asset_file = plugin_dir_path( __FILE__ ) . 'build/index.asset.php';
	if ( ! file_exists( $asset_file ) ) {
		return;
	}
	$asset = include $asset_file;
	wp_enqueue_script(
		'my-highlight-editor',
		plugins_url( 'build/index.js', __FILE__ ),
		$asset['dependencies'],
		$asset['version'],
		true
	);
} );

add_filter( 'render_block_core/paragraph', function ( $block_content, $block ) {
	if ( empty( $block['attrs']['isHighlighted'] ) ) {
		return $block_content;
	}
	$processor = new WP_HTML_Tag_Processor( $block_content );
	if ( $processor->next_tag() ) {
		$processor->add_class( 'is-highlighted' );
	}
	return $processor->get_updated_html();
}, 10, 2 );

保存側でクラスを付ける方法として blocks.getSaveContent.extraProps フィルターもありますが、保存される HTML そのものが変わるため、フィルターの内容を後から変えたり、プラグインを停止したりしたときに、既存の投稿でブロックの検証エラーが出る場合があります。表示時に render_block_{ブロック名} フィルターでクラスを足す方法なら、保存される HTML は変わらず、プラグインを停止しても属性が無視されるだけで済みます。HTML の書き換えには文字列置換ではなく WP_HTML_Tag_Processor を使うと、属性の順序や既存のクラスを崩さずに済みます。

.is-highlighted の見た目を指定する CSS は、公開画面とエディターの両方に読み込みます。エディターの本文が iframe の中に表示される環境では、enqueue_block_assets フックで読み込むと iframe 内にも適用されます。

withSelectとuseSelectの違い

データを取得する HOC の例も見ておきます。投稿タイトルの文字数を表示するコンポーネントを withSelect で書くと次のようになります。

import { withSelect, useSelect } from '@wordpress/data';

// HOC で書く場合
const TitleLengthHoc = ( { title } ) => <p>{ title.length }文字</p>;
export const TitleLength = withSelect( ( select ) => ( {
	title: select( 'core/editor' ).getEditedPostAttribute( 'title' ) || '',
} ) )( TitleLengthHoc );

// フックで書く場合
export function TitleLengthWithHook() {
	const title = useSelect(
		( select ) =>
			select( 'core/editor' ).getEditedPostAttribute( 'title' ) || '',
		[]
	);
	return <p>{ title.length }文字</p>;
}

結果は同じですが、フックのほうが props の受け渡しが見えやすく、コンポーネントの入れ子も増えません。既存コードで withSelect や compose を見かけたら HOC だと理解し、新しく書く部分はフックを選ぶ、という使い分けが現実的です。

つまずきやすい点と確認方法

  • フィルターが効かない: blocks.registerBlockType は、対象のブロックが登録される前に追加されている必要があります。コアブロックはエディターの初期化時に登録されるため、enqueue_block_editor_assets で読み込めば間に合いますが、他のプラグインのブロックを対象にする場合は読み込み順を確認してください。
  • HOC を毎回作り直している: HOC はモジュールの最上位で一度だけ作ります。コンポーネントの中で HOC を呼ぶと、描画のたびに別のコンポーネントとして扱われ、入力中のフォーカスが外れるなどの不具合が起きます。
  • 名前空間の重複: addFilter の第2引数は他と重ならない名前にします。同じ名前で登録すると、後から登録したものに置き換わります。
  • 確認方法: ブラウザの開発者ツールのコンソールでエラーが出ていないかを見て、React の開発者ツールで HOC の表示名が付いたコンポーネントがブロックを包んでいるかを確認します。保存後は、コードエディター表示でブロックのコメントに "isHighlighted":true が入っているかも確かめておくと、属性の保存と表示のどちらに問題があるか切り分けられます。

まとめ

  • HOC はコンポーネントを受け取って機能を足したコンポーネントを返す関数で、WordPress では createHigherOrderComponent で作ります。
  • コアブロックの編集画面に手を加えるには、editor.BlockEdit や editor.BlockListBlock に HOC を addFilter で登録します。
  • 対象外のブロックには props をそのまま渡し、全ブロックへの影響を避けます。
  • 公開画面への反映は render_block_{ブロック名} と WP_HTML_Tag_Processor で行うと、保存済みの投稿で検証エラーが出ません。
  • データの取得は withSelect より useSelect などのフックが現在の主流です。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲