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>
);
}| Komponen | Fungsi |
|---|---|
<InnerBlocks /> | Area editor untuk menambahkan nested blocks |
allowedBlocks | Array 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
| Prop | Fungsi |
|---|---|
value | Nilai teks saat ini (dari attributes) |
onChange | Handler perubahan teks → setAttributes() |
tagName | Tag HTML output (h1, h2, p, a, dll.) |
className | Class CSS dengan template literal |
allowedFormats | Format yang diizinkan (core/bold, core/italic) |
BlockControls & Toolbar
| Komponen | Fungsi |
|---|---|
BlockControls | Container toolbar yang muncul di atas block |
ToolbarGroup | Grup tombol dalam toolbar |
ToolbarButton | Tombol individual (Large, Medium, Small) |
isPressed | Boolean — tombol aktif atau tidak |
onClick | Handler 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
| Konsep | Penjelasan |
|---|---|
| InnerBlocks | Menyarangkan block di dalam block lain |
allowedBlocks | Membatasi block types yang bisa dinest |
| JSXBlock class | Class PHP reusable untuk registrasi block |
| RichText | Komponen editor teks kaya (bold, italic) |
| BlockControls | Toolbar di atas block untuk opsi (size S/M/L) |
isPressed | Menandai tombol toolbar yang aktif |
| Block Recovery | Masalah saat save output berubah setelah block tersimpan |