Skip to content

InnerBlocks, Reusable JSXBlock Class & Generic Heading Block

Konsep InnerBlocks

InnerBlocks memungkinkan sebuah block menyarangkan (nest) block lain di dalamnya. Ini adalah kunci untuk membuat layout yang fleksibel.

Import InnerBlocks

js
import { InnerBlocks } from "@wordpress/block-editor";

Penggunaan di Edit Component

js
function EditComponent() {
  return (
    <div className="page-banner">
      <div className="page-banner__content container">
        <InnerBlocks allowedBlocks={["ourBlockTheme/genericHeading", "ourBlockTheme/genericButton"]} />
      </div>
    </div>
  );
}

Penggunaan di Save Component

js
function SaveComponent() {
  return (
    <div className="page-banner">
      <div className="page-banner__content container">
        <InnerBlocks.Content />
      </div>
    </div>
  );
}
KomponenFungsi
<InnerBlocks />Area editor untuk menambahkan nested blocks
allowedBlocksArray block types yang diizinkan di dalamnya
<InnerBlocks.Content />Menyimpan konten nested blocks ke database

JSXBlock: Class PHP yang Reusable

Daripada menulis kode registrasi block yang sama berulang-ulang, buat class PHP reusable:

php
class JSXBlock {
    function __construct($name) {
        $this->name = $name;
        add_action('init', [$this, 'onInit']);
    }
    
    function onInit() {
        wp_register_script(
            $this->name,
            get_stylesheet_directory_uri() . "/build/{$this->name}.js",
            array('wp-blocks', 'wp-editor')
        );
        
        register_block_type("ourBlockTheme/{$this->name}", array(
            'editor_script' => $this->name
        ));
    }
}

Penggunaan

php
// Cukup satu baris per block!
new JSXBlock("banner");
new JSXBlock("genericHeading");
new JSXBlock("genericButton");

Update package.json

Setiap block baru harus ditambahkan sebagai entry point:

json
{
  "scripts": {
    "start": "wp-scripts start src/index.js our-blocks/banner.js our-blocks/genericHeading.js",
    "build": "wp-scripts build src/index.js our-blocks/banner.js our-blocks/genericHeading.js"
  }
}

Generic Heading Block

Tujuan

Block heading yang bisa:

  • Diedit langsung di editor (RichText)
  • Memilih ukuran: Large (h1), Medium (h2), Small (h3)
  • Mendukung bold dan italic

Attributes

js
wp.blocks.registerBlockType("ourBlockTheme/genericHeading", {
  title: "Generic Heading",
  attributes: {
    text: { type: "string" },
    size: { type: "string", default: "large" }
  },
  edit: EditComponent,
  save: SaveComponent
});

Import yang Diperlukan

js
import { RichText, BlockControls } from "@wordpress/block-editor";
import { ToolbarGroup, ToolbarButton } from "@wordpress/components";

Edit Component

js
function EditComponent(props) {
  function handleTextChange(x) {
    props.setAttributes({ text: x });
  }
  
  return (
    <>
      <BlockControls>
        <ToolbarGroup>
          <ToolbarButton
            isPressed={props.attributes.size === "large"}
            onClick={() => props.setAttributes({ size: "large" })}
          >
            Large
          </ToolbarButton>
          <ToolbarButton
            isPressed={props.attributes.size === "medium"}
            onClick={() => props.setAttributes({ size: "medium" })}
          >
            Medium
          </ToolbarButton>
          <ToolbarButton
            isPressed={props.attributes.size === "small"}
            onClick={() => props.setAttributes({ size: "small" })}
          >
            Small
          </ToolbarButton>
        </ToolbarGroup>
      </BlockControls>
      
      <RichText
        allowedFormats={["core/bold", "core/italic"]}
        tagName="h1"
        className={`headline headline--${props.attributes.size}`}
        value={props.attributes.text}
        onChange={handleTextChange}
      />
    </>
  );
}

Save Component

js
function SaveComponent(props) {
  function createTagName() {
    switch (props.attributes.size) {
      case "large":
        return "h1";
      case "medium":
        return "h2";
      case "small":
        return "h3";
    }
  }
  
  return (
    <RichText.Content
      tagName={createTagName()}
      className={`headline headline--${props.attributes.size}`}
      value={props.attributes.text}
    />
  );
}

Komponen-Komponen Penting

RichText

PropFungsi
valueNilai teks saat ini (dari attributes)
onChangeHandler perubahan teks → setAttributes()
tagNameTag HTML output (h1, h2, p, a, dll.)
classNameClass CSS dengan template literal
allowedFormatsFormat yang diizinkan (core/bold, core/italic)

BlockControls & Toolbar

KomponenFungsi
BlockControlsContainer toolbar yang muncul di atas block
ToolbarGroupGrup tombol dalam toolbar
ToolbarButtonTombol individual (Large, Medium, Small)
isPressedBoolean — tombol aktif atau tidak
onClickHandler klik → setAttributes()

RichText.Content (untuk Save)

js
<RichText.Content
  tagName={createTagName()}   // Tag HTML dinamis
  className={...}              // Class CSS
  value={props.attributes.text} // Teks yang disimpan
/>

Masalah Block Recovery

Jika Anda mengubah output SaveComponent setelah block sudah disimpan di database, WordPress akan mendeteksi perbedaan antara HTML yang tersimpan dan HTML yang diharapkan. Ini menyebabkan pesan error "Attempt Block Recovery".

Ini adalah salah satu alasan mengapa nanti kita akan beralih ke PHP Render Callback — agar perubahan HTML tidak menyebabkan masalah pada post yang sudah ada.


Kesimpulan

KonsepPenjelasan
InnerBlocksMenyarangkan block di dalam block lain
allowedBlocksMembatasi block types yang bisa dinest
JSXBlock classClass PHP reusable untuk registrasi block
RichTextKomponen editor teks kaya (bold, italic)
BlockControlsToolbar di atas block untuk opsi (size S/M/L)
isPressedMenandai tombol toolbar yang aktif
Block RecoveryMasalah saat save output berubah setelah block tersimpan