edit と save

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

edit edit

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

ESNext

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

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

    // ...

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

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

ES5

var blockSettings = {
    apiVersion: 2,

    // ...

    edit: function () {
        var blockProps = wp.blockEditor.useBlockProps();

        return wp.element.createElement( 'div', blockProps, 'Your block.' );
    },
};

Top ↑

ブロックラッパー props ブロックラッパー props

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

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

ESNext

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

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

    // ...

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

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

ES5

var blockSettings = {
    apiVersion: 2,

    // ...

    edit: function () {
        var blockProps = wp.blockEditor.useBlockProps( {
            className: 'my-random-classname',
        } );

        return wp.element.createElement( 'div', blockProps, 'Your block.' );
    },
};

Top ↑

属性 属性

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

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

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

ESNext

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

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

ES5

edit: function( props ) {
    var blockProps = wp.blockEditor.useBlockProps();

    return wp.element.createElement(
        'div',
        blockProps,
        props.attributes.content
    );
}

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

Top ↑

isSelected isSelected

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

ESNext

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

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

ES5

edit: function( props ) {
    var blockProps = wp.blockEditor.useBlockProps();

    return wp.element.createElement(
        'div',
        blockProps,
        [
            'Your block.',
            props.isSelected ? wp.element.createElement(
                'span',
                null,
                'Shows only when the block is selected.'
            )
        ]
    );
}

Top ↑

setAttributes setAttributes

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

ESNext

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>
    );
};

ES5

edit: function( props ) {
    var blockProps = wp.blockEditor.useBlockProps();

    // Simplify access to attributes
    let content = props.attributes.content;
    let mySetting = props.attributes.mySetting;

    // Toggle a setting when the user clicks the button
    let toggleSetting = () => props.setAttributes( { mySetting: ! mySetting } );
    return wp.element.createElement(
        'div',
        blockProps,
        [
            content,
            props.isSelected ? wp.element.createElement(
                'button',
                { onClick: toggleSetting },
                'Toggle setting'
            ) : null
        ]
    );
},

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

ESNext

// 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 } );
};

ES5

// Good - cloning the old list
var newList = attributes.list.slice();

var addListItem = function ( newListItem ) {
    setAttributes( { list: newList.concat( [ newListItem ] ) } );
};

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

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

Top ↑

save save

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

ESNext

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

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

ES5

save: function() {
    var blockProps = wp.blockEditor.useBlockProps.save();

    return wp.element.createElement(
        'div',
        blockProps,
        'Your block.'
    );
}

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

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

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

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

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

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

Top ↑

ブロックラッパー props ブロックラッパー props

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

Top ↑

attributes attributes

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

ESNext

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

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

ES5

save: function( props ) {
    var blockProps = wp.blockEditor.useBlockProps.save();

    return wp.element.createElement(
        'div',
        blockProps,
        props.attributes.content
    );
}

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

Top ↑

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

Top ↑

子要素への属性の保存 子要素への属性の保存

ESNext

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 }
            />
        </p>
    );
},

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

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

ES5

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

edit: function( props ) {
    var blockProps = wp.blockEditor.useBlockProps();
    var updateFieldValue = function( val ) {
        props.setAttributes( { content: val } );
    }

    return wp.element.createElement(
        'div',
        blockProps,
        wp.element.createElement(
            wp.components.TextControl,
            {
                label: 'My Text Field',
                value: props.attributes.content,
                onChange: updateFieldValue,

            }
        )
    );
},

save: function( props ) {
    var blockProps = wp.blockEditor.useBlockProps.save();

    return wp.element.createElement( 'div', blockProps, props.attributes.content );
},

Top ↑

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

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

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

ESNext

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 ) } );
                }}
            />
        </p>
    );
},

save: () => {
    return null;
}

ES5

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

edit: function( props ) {
    var blockProps = wp.blockEditor.useBlockProps();

    return wp.element.createEleement(
        'div',
        blockProps,
        wp.element.createElement(
            wp.components.TextControl,
            {
                label: 'Number Posts to Show',
                value: props.attributes.postsToShow,
                onChange: function( val ) {
                    props.setAttributes( { postsToShow: parseInt( val ) } );
                },
            }
        )
    );
},

save: function() {
    return null;
}

Top ↑

妥当性検証 (Validation) 妥当性検証 (Validation)

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

ブロックが不正とマークされると、ユーザーには妥当性検証の失敗をどのように処理するか求められます。

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

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

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

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

Top ↑

妥当性検証 FAQ 妥当性検証 FAQ

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

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

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

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

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

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

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

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

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

原文

最終更新日: