edit と save

Topics

  • edit
    • ブロックラッパー props
    • 属性
    • isSelected
    • setAttributes
  • save
    • ブロックラッパー props
    • attributes
    • 子要素への属性の保存
    • シリアライゼーションを通じた属性の保存
  • バリデーション
    • バリデーション FAQ

クライアント上で JavaScript を使用してブロックを登録する際、edit 関数と save 関数は、ブロックがどのようにレンダー、操作、保存されるかのインターフェースを提供します。

edit

edit 関数はエディターのコンテキスト内でのブロックの構造を記述します。ブロックが使用される際、エディターがどのようにブロックをレンダーするかを表します。

import { useBlockProps } from '@wordpress/block-editor';

// ...
const blockSettings = {
	apiVersion: 3,

	// ...

	edit: () => {
		const blockProps = useBlockProps();

		return <div { ...blockProps }>Your block.</div>;
	},
};

Top ↑

ブロックラッパー props

ここで最初に注意するのが、ブロックラッパー要素での useBlockProps React フックの使用です。上の例でブロックラッパーはエディター内に div をレンダーしますが、Gutenberg エディターがどのようにブロックを操作すべきか知らせるために、ブロックに必要な追加の className を加えます。すなわち、ブロックラッパー要素は useBlockProps React フックコールから取得した props を適用する必要があります。ブロックラッパー要素はネイティブの DOM 要素、例えば <div> や <table> か、または、任意の追加 props をネイティブの DOM 要素にフォワードする React コンポーネントでなければなりません。たとえば、<Fragment> や <ServerSideRender> コンポーネントは使用できません。

要素ラッパーで追加のカスタム HTML 属性が必要であれば、useBlockProps フックに引数として追加します。たとえば次のコードはラッパーに my-random-classname className を追加します。

import { useBlockProps } from '@wordpress/block-editor';

// ...
const blockSettings = {
	apiVersion: 3,

	// ...

	edit: () => {
		const blockProps = useBlockProps( {
			className: 'my-random-classname',
		} );

		return <div { ...blockProps }>Your block.</div>;
	},
};

Top ↑

属性

edit 関数はまたオブジェクト引数を通じて多くのプロパティを受け取ります。このプロパティを使用してブロックの振る舞いを変更できます。

attributes プロパティはすべての利用可能な属性と対応する値を表します。属性はブロックタイプ登録の際に attributes プロパティで記述されます。属性ソースを指定する方法については属性のドキュメントを参照してください。

この例ではブロック登録の際に content 属性を定義したと仮定し、edit 関数内で値を受け取って使用します。

edit: ( { attributes } ) => {
	const blockProps = useBlockProps();

	return <div { ...blockProps }>{ attributes.content }</div>;
};

エディターにブロックを追加すると、attributes.content の値は div 内部に表示されます。

Top ↑

isSelected

isSelected プロパティはブロックが現在選択されているかどうかを伝えるブール値です。

edit: ( { attributes, isSelected } ) => {
	const blockProps = useBlockProps();

	return (
		<div { ...blockProps }>
			Your block.
			{ isSelected && (
				<span>Shows only when the block is selected.</span>
			) }
		</div>
	);
};

Top ↑

setAttributes

ブロックは setAttributes 関数を使用して、ユーザーの操作に基づいて、個々の属性を更新できます。

edit: ( { attributes, setAttributes, isSelected } ) => {
	const blockProps = useBlockProps();

	// Simplify access to attributes
	const { content, mySetting } = attributes;

	// Toggle a setting when the user clicks the button
	const toggleSetting = () => setAttributes( { mySetting: ! mySetting } );
	return (
		<div { ...blockProps }>
			{ content }
			{ isSelected && (
				<button onClick={ toggleSetting }>Toggle setting</button>
			) }
		</div>
	);
};

オブジェクトや配列の属性を使用する場合には、更新の前に属性をコピーするかクローンしてください。

// Good - a new array is created from the old list attribute and a new list item:
const { list } = attributes;
const addListItem = ( newListItem ) =>
	setAttributes( { list: [ ...list, newListItem ] } );

// Bad - the list from the existing attribute is modified directly to add the new list item:
const { list } = attributes;
const addListItem = ( newListItem ) => {
	list.push( newListItem );
	setAttributes( { list } );
};

コピーやクローンが必要なのはなぜでしょうか ? JavaScript では配列やオブジェクトは参照渡しされるため、コピーやクローンを行うことで変更が同じデータへの参照を持つ他のコードに影響を与えないことが保証されます。さらに Gutenberg プロジェクトは Redux ライブラリの哲学、state は不変でなければならない に従っています。データは直接変更せず、変更を含む新しいバージョンのデータを作る必要があります。

Top ↑

save

save 関数は、最終的なマークアップに異なる属性を結合する方法を定義します。この属性は post_content 内にシリアライズされます。

save: () => {
	const blockProps = useBlockProps.save();

	return <div { ...blockProps }> Your block. </div>;
};

ほとんどのブロックで save の戻り値は、ブロックがサイトのフロントエンドでどのように表示されるかを示す WordPress Element のインスタンス になります。

注意: save から文字列値を返すことができますが、この値は エスケープされます。文字列が HTML マークアップを含む場合、サイトのフロントエンドには、同等の HTML ノードコンテンツではなくマークアップがそのまま表示されます。save から生の HTML を返す必要がある場合は wp.element.RawHTML を使用してください。名前が示すとおり、これは クロスサイトスクリプティング が発生しやすいため、可能な場合は WordPress Element 階層の使用を推奨します。

注意: save 関数は、呼び出し時に使用された属性にのみ依存する純粋関数でなければなりません。どのようなサイドイフェクトも与えられず、別のソースからの情報も取得できません。たとえば 内部でデータモジュール select( store ).selector( ... ) を使用することはできません。
これは外部の情報が変更されると、あとで投稿を編集する際にブロックが不正 (invalid) としてマーク付けされる可能性があるためです。詳細には以下の「バリデーション」を参照してください。
保存の流れで他の情報が必要になった場合、開発者には2つの選択肢があります。

  • ダイナミックブロック を使用してサーバー上で動的に必要な情報を取得する。
  • 外部の値を属性として保存し、変更があった場合にはブロックの edit 関数内で動的に更新する。

ダイナミックブロック の場合、save の戻り値は、ブロックを実装するプラグインが無効化された場合に表示されるブロックコンテンツの、キャッシュしたコピーを返すことができます。

特に指定しない場合、デフォルトの実装ではダイナミックブロックの投稿コンテンツにはマークアップは保存されず、代わりにブロックがサイトのフロントエンド側で表示された際に常に計算するよう延期されます。

Top ↑

ブロックラッパー props

edit 関数同様、静的ブロックをレンダーする際は、ブロックのラッパー要素に useBlockProps.save() から返されるブロック props を追加することが重要です。これでブロックサポート API から外挿された任意の HTML 属性に加えて、ブロッククラス名が正しくレンダーされます。

Top ↑

attributes

edit 関数と同様 save 関数もまたオブジェクト引数を受け取ります。オブジェクト引数にはマークアップに挿入することができる属性が含まれます。

save: ( { attributes } ) => {
	const blockProps = useBlockProps.save();

	return <div { ...blockProps }>{ attributes.content }</div>;
};

ブロックを保存する際、属性は、属性ソース定義で指定した形式で保存されます。属性ソースが指定されていない場合、属性はブロックのコメントデリミッターに保存されます。詳細は ブロック属性のドキュメント を参照してください。

Top ↑

属性、editsave を一緒に使用する例をいくつか挙げます。完全に動作するサンプルはブロックのチュートリアルの「属性と編集可能フィールド

Top ↑

子要素への属性の保存

attributes: {
	content: {
		type: 'string',
		source: 'html',
		selector: 'div'
	}
},

edit: ( { attributes, setAttributes } ) => {
	const blockProps = useBlockProps();
	const updateFieldValue = ( val ) => {
		setAttributes( { content: val } );
	}
	return (
		<div { ...blockProps }>
			<TextControl
				label='My Text Field'
				value={ attributes.content }
				onChange={ updateFieldValue }
			/>
		</div>
	);
},

save: ( { attributes } ) => {
	const blockProps = useBlockProps.save();

	return <div { ...blockProps }> { attributes.content } </div>;
},

Top ↑

シリアライゼーションを通じた属性の保存

理想的には保存する属性はマークアップに含まれるべきですが、常に現実的ではありません。このため属性ソースが指定されない場合、属性はシリアライズされブロックのコメントデリミッターに保存されます。

次の例は「最近の投稿」ブロックのような、マークアップをサーバーサイドでレンダーするダイナミックブロックになります。save 関数は依然として必要ですが、ブロックはエディターからコンテンツを保存していないため、この例では単純に null を返しています。

attributes: {
	postsToShow: {
		type: 'number',
	}
},

edit: ( { attributes, setAttributes } ) => {
	const blockProps = useBlockProps();

	return (
		<div { ...blockProps }>
			<TextControl
				label='Number Posts to Show'
				value={ attributes.postsToShow }
				onChange={ ( val ) => {
					setAttributes( { postsToShow: parseInt( val ) } );
				}}
			/>
		</div>
	);
},

save: () => {
	return null;
}

Top ↑

バリデーション

エディターがブロックをロードする際、コンテンツの消失を防止するため投稿コンテンツ内のすべてのブロックは検証 (validate) され、その正しさが確かめられます。これはブロックを保存する実装と密接な関係があります。なぜならエディターが正しくブロックをリストアしなければ、ユーザーは意図せずにコンテンツを削除したり、変更するためです。エディターの初期化中、各ブロックのマークアップは、投稿コンテンツからパースされた属性を使用して再生成されます。新しく生成されたマークアップが投稿コンテンツ内の保存済みマークアップと異なる場合、ブロックは不正 (invalid) とマークされます。これはユーザーが編集していない限り、マークアップは保存されたコンテンツと同じはずだと仮定しているためです。

ブロックが不正とマークされると、ユーザーにはバリデーションの失敗をどのように処理するか求められます。

不正なブロックのプロンプト

ブロックのリカバリーを試行 ボタンをクリックすると、できる限りの修復のアクションを試みます。

ブロック側の横の3ドットメニューをクリックすると、3つのオプションが表示されます。

  • 「解決」ボタンをクリックすると「ブロックの問題を解決」ダイアログが開き、2つのオプションを選択できます。
    • HTML に変換: 投稿コンテンツ内の保存済みオリジナルのマークアップを保護し、ブロックをオリジナルのブロックタイプから HTML ブロックタイプに変換します。ユーザーは HTML マークアップを直接変更できます。
    • ブロックへ変換: 投稿コンテンツ内の保存済みオリジナルのマークアップを保護し、ブロックをオリジナルのブロックタイプから検証済みのブロックタイプに変換します。
  • HTML に変換: 投稿コンテンツ内の保存済みオリジナルのマークアップを保護し、ブロックをオリジナルのブロックタイプから HTML ブロックタイプに変換します。ユーザーは HTML マークアップを直接変更できます。
  • クラシックブロックに変換: 投稿コンテンツ内の保存済みオリジナルのマークアップを正しいものとして保護します。ブロックはオリジナルのブロックタイプからクラシックブロックタイプに変換されるため、オリジナルのブロックタイプで利用可能だったコントロールでコンテンツを編集できない可能性があります。

Top ↑

バリデーション FAQ

ブロックが不正になるのはどのような場合ですか?

ブロックが不正になる原因には大きく2つあります。

  1. ブロックのコードのフローが、コンテンツの意図しない変更を引き起こした。以下の質問「プラグイン作者です。プラグインが invalid とマークされたらどうやってデバッグすればいいですか ?」を参照してください。
  2. ユーザーまたは外部のエディターがブロックの HTML マークアップを変更して不正となった。

プラグイン作者です。プラグインが invalid とマークされたらどうやってデバッグすればいいですか ?

デバッグを始める前に、上に記述されたバリデーションのステップと、ブロックが不正と検知されるプロセスについて理解してください。ブロックが不正となるのは再生成されたマークアップが投稿コンテンツ内の保存済みマークアップと合致しない場合です。したがって保存されたコンテンツからブロックの属性が正しくパースされなかった場合にしばしば発生します。

属性ソースを使用している場合には、マークアップのソースの属性が期待したとおりに正しいタイプ (通常は 'string' か 'number') で保存されていることを確認してください。

ブロックの不正が検知されるとブラウザーの開発者ツールコンソールに警告が出力されます。警告にはマークアップの相違が発生した正確な場所の詳細が含まれます。期待したマークアップと実際のマークアップの違いを比較し、どこで問題が発生したかを調べてください。

ブロックの save の動きを変えたら古いコンテンツが不正なブロックになりました。どのように修正すればよいですか ?

ブロックの非推奨プロセス のガイドを参照して、意図したマークアップの変更に古いコンテンツを収容する方法を学習してください。

原文

最終更新日: