セットしたはずのグラデーションが表示されない?!

まずはPanelColorGradientSettingsを使ってみよう

ブロック開発でカスタムブロックに色を設定するのは基本中の基本ですよね。 まず、最初のカスタマイズは色の設定でしょう。 Wordpressはそのための入力用コントロールとしてPanelColorGradientSettingsというコントロールを用意してくれています。 これが優れもので、中間色はもちろんのこと、グラデーションまでセットできます。 これを使うようになってグラデーションを採用することが増えました。

このコントロールを使って、単色でもグラデーションでも選択できるようにするには、次のような手順を踏みます。 1. 色情報を格納するためにblock.json内にattributesを設定する。 2. サイドバーにPanelColorGradientSettingsを設置する。 3. ユーザーが選択した色をbackGroundスタイルにセットする。

具体的なコードで見てみましょう。 まず、block.jsonにこんなふうに入れます

"attributes": {
    "bg_Color": {
        "type": "string",
        "default": "#504237"
    },
    "bg_Gradient": {
        "type": "string",
    }
}

attributesには初期値も設定できるんです。ですから、このように設定すると、最初にブロックがマウントされてattributesが呼び出されるとbg_Colorには#504237がセットされた状態になるのです。便利なので重宝します。

つぎにPanelColorGradientSettingsの設置です。 edit.jsのreturn文にInspectorControlsを用意してその中に次のコードを入力してやります。

<PanelColorGradientSettings
    title={__("Background Color Setting","text-domain")}
    settings={[
        {
            colorValue: bg_Color,
            gradientValue: bg_Gradient,

            label: __("Choice color or gradient","text-domain"),
            onColorChange: (newValue) => {
                setAttributes({ bg_Color: newValue });
            },
            onGradientChange: (newValue) => {
                setAttributes({ bg_Gradient: newValue });
            },
        }
    ]}
/>

これで取得したプロパティ値をDOM要素のstyle属性としてセットすればあっという間にインラインスタイルが出来上がります。 次のような感じです。

const bgColor = bg_Color || bg_Gradient;
return(
    <div style={{ backGround: bgColor }}></div>
)

なんとも小気味よくセットできるのです。

グラデーションが表示されない

しかし、ふと見るとこんな現象が起こっています。

image

お判りでしょうか。サイドバーには確かに選択したグラデーションが表示されているのに、レンダリングされたブロックは単色です。 この色はデフォルトで設定した色です。 なぜ、こんなことが起こるのか全く分かりません。

そしてさらにsave.jsに

<div style={{ background: bgColor }}></div>

などとするとBlock validation failedというエラーを起こします。これはエディタでブロックが保存された状態と、それをレンダリングしようとする状態とで一致しない場合に発生します。リカバリを試みることはできますが、毎回そんなことをしているわけにはいかないし、実際リカバリもできもせん。 途方に暮れてしまいました。

原因の解明

何とか原因が究明できましたので、ここでそれを解説します。

PanelColorGradientSettingsは単色を選択するとその色のコードを、グラデーションを選択するとlinear-gradientまたはradial-gradientを返します。そして選択しなかった方はundefinedを返すのです。 つまり、単色が選択されると、bg_Colorには色コードがセットされますが、グラデーションが選択されるとundefinedがセットされます。 この仕組みが元凶でした。 もう少し具体的に説明します。 グラデーションを選択したとします。 セットしたときはbg_Colorにundefinedがセットされ、bg_Gradientには、linear-gradientまたはradial-gradientがセットされます。 そして、

const bgColor = bg_Color || bg_Gradient;

が実行されれば、無事にbgColorにはグラデーションのスタイルがセットされ、ブロックにはグラデーションがレンダリングされます。 しかし、その状態でブロックが保存され、次にそのブロックがマウントされるとどうなるでしょうか。 ここで、あの重宝していた初期値の設定が仇になります。 bg_Colorはundefinedのままでいてくれません。attributesで設定した初期値である#504237がセットされるのです。そしてconst bgColor = bg_Color || bg_Gradient;が実行されるとbgColorには bg_Colorがセットされてしまって bg_Gradientは無視されてしまいます。 これが、奇妙な現象の原因でした。

対策を講じたコード

そしてようやく結論です。

<PanelColorGradientSettings
    title={__("Background Color Setting")}
    settings={[
        {
            colorValue: bg_Color,
            gradientValue: bg_Gradient,

            label: __("Choice color or gradient"),
            onColorChange: (newValue) => {
                setAttributes({ bg_Color: newValue === undefined ? '' : newValue });
            },
            onGradientChange: (newValue) => {
                setAttributes({ bg_Gradient: newValue });
            },
        }
    ]}
/>

おわかりでしょうか。onColorChangeのコールバック関数のsetAttributesでセットする値をnewValueがundefinedの場合、つまり、選択されなかった場合は空文字にするのです。undefinedでなければ、初期値のセットは行われません。 そして空文字はfalsy values(falseと評価される値)なのでconst bgColor = bg_Color || bg_Gradient;ではbg_Gradientが選択されるというわけです。

前にattributesに色を設定するときは、初期値の設定には注意しないといけないというようなことを聞いた記憶があるのですが、その時はその意味も理解せずスルーしていました。 しかし、こんなしっぺ返しを食らうとは思ってもみませんでした。