1Em artigos anteriores deste blog, exploramos o desenvolvimento de blocos do WordPress de vários ângulos. Examinamos o desenvolvimento de blocos estáticos e dinâmicos e ampliamos a funcionalidade dos blocos principais. No entanto, a abordagem que adotamos até agora nos permitiu essencialmente criar blocos padrão, que não reagiam às interações do usuário em tempo real. Em resumo, esses blocos eram não interativos.

Neste artigo, exploraremos uma nova abordagem para o desenvolvimento de blocos, que nos permitirá criar blocos interativos graças a uma nova e poderosa API do WordPress: a API de interatividade do WordPress. Introduzida no WordPress 6.5, essa API permite que você crie blocos que reagem em tempo real às interações do usuário, possibilitando a criação de experiências ricas para o usuário e tornando seus sites atraentes, dinâmicos e envolventes.

Há muito o que abordar, mas antes de começarmos, vejamos os requisitos essenciais!

O que você precisa antes de começar a usar a API de interatividade

Como a API Interactivi é baseada em React, você precisará de pelo menos um conhecimento básico de JavaScript do lado do servidor e React, além de ferramentas de build como npm e npx. Também é necessário compreender bem o desenvolvimento em WordPress e do editor de blocos Gutenberg.

Depois de adquirir as habilidades necessárias, você precisará de um ambiente de desenvolvimento local que permita o lançamento rápido e fácil de um site WordPress. Recomendamos o DevKinsta, nossa suíte de desenvolvimento local criada especificamente para o WordPress. Com o DevKinsta, você pode configurar um novo site local do WordPress com apenas alguns cliques e personalizá-lo em detalhes.

Ao criar um novo projeto WordPress no DevKinsta, você pode definir as seguintes opções:

  • Domínio de nível superior: Padrão .local
  • Versão do PHP
  • Nome do banco de dados
  • Habilitar HTTPS
  • Detalhes do WordPress
  • Atualização automática do WordPress
  • Multisite

Além disso, você pode importar um site MyKinsta existente a partir de um backup.

Configuração de um site local no DevKinsta
Configuração de um site local no DevKinsta.

O que é a API de interatividade?

A API de Interatividade é uma API nativa do WordPress que permite que você adicione interatividade aos blocos do Gutenberg e, consequentemente, aos artigos e páginas de um site WordPress. É uma solução leve e moderna que adota uma abordagem declarativa para gerenciar interações do usuário.

Para criar um bloco interativo do zero, você precisa de habilidades avançadas de desenvolvimento em PHP e JavaScript no lado do servidor. Entretanto, você não precisa reinventar a roda a cada novo projeto, pois o WordPress fornece um modelo para a criação de blocos interativos:

npx @wordpress/create-block --template @wordpress/create-block-interactive-template

Esse modelo inclui tudo o que você precisa para criar um bloco interativo, inclusive dois exemplos funcionais que podem ser usados como referência para o seu primeiro projeto: um botão para alternar o tema atual e um botão para expandir/recolher um parágrafo.

Para começar, abra sua ferramenta de linha de comando favorita, navegue até o diretório Plugins de sua instalação local do WordPress e digite o seguinte:

npx @wordpress/create-block your-interactive-block --template @wordpress/create-block-interactive-template

Aguarde alguns instantes para que a instalação seja concluída e, em seguida, abra a pasta do projeto usando seu editor de código preferido. Recomendamos o uso do Visual Studio Code, mas você pode usar o editor com o qual se sentir mais confortável.

Um bloco interativo no Visual Studio Code
O projeto de bloco interativo fornecido pelo @wordpress/create-block-interactive-template

Na linha de comando, navegue até a pasta do novo plugin e inicie o servidor de desenvolvimento usando o seguinte comando:

npm start

A partir de agora, todas as alterações que você fizer no seu bloco ficarão visíveis em tempo real na sua instalação do WordPress.

Em seguida, no administrador do WordPress, navegue até a tela Plugins e ative o plugin Interactivity API que você acabou de criar. Crie um novo artigo ou página, procure o bloco Your interactive block no inseridor de blocos e adicione-o ao conteúdo. Salve o artigo e visualize no frontend. Você verá um bloco amarelo com dois botões. O primeiro botão altera a cor de fundo do bloco e o segundo botão mostra ou oculta o conteúdo do parágrafo.

Exemplo de bloco interativo
Exemplo de bloco interativo — fornecido pelo template @wordpress/create-block-interactive-template.

Agora que você tem um plugin de referência para os tópicos abordados neste artigo, podemos prosseguir e explorar os blocos interativos em maior profundidade.

A estrutura dos blocos interativos

A estrutura dos blocos interativos é a mesma dos blocos tradicionais. Você ainda precisará dos seguintes arquivos: package.json, block.json, edit.js e style.scss. Além disso, você precisará de um arquivo render.php para renderização no lado do servidor e um arquivo view.js para lidar com a interatividade do frontend.

Vamos analisar as partes principais de um bloco interativo, detalhando os arquivos do projeto inicial.

package.json

O arquivo package.json é usado em projetos Node para identificar seu projeto, gerenciar scripts e gerenciar e instalar dependências durante o desenvolvimento.

A seguir, você encontra o package.json para o bloco interativo fornecido pelo create-block-interactive-template:

{
	"name": "your-interactive-block",
	"version": "0.1.0",
	"description": "An interactive block with the Interactivity API.",
	"author": "The WordPress Contributors",
	"license": "GPL-2.0-or-later",
	"main": "build/index.js",
	"scripts": {
		"build": "wp-scripts build --experimental-modules",
		"format": "wp-scripts format",
		"lint:css": "wp-scripts lint-style",
		"lint:js": "wp-scripts lint-js",
		"packages-update": "wp-scripts packages-update",
		"plugin-zip": "wp-scripts plugin-zip",
		"start": "wp-scripts start --experimental-modules"
	},
	"dependencies": {
		"@wordpress/interactivity": "latest"
	},
	"files": [
		"[^.]*"
	],
	"devDependencies": {
		"@wordpress/scripts": "^30.24.0"
	}
}

As seções scripts e dependencies são particularmente importantes aqui.

  • build: compila o código-fonte em JavaScript para produção. A opção --experimental-modules habilita o suporte para módulos de script do WordPress.
  • start: inicia o servidor de desenvolvimento. Observe que a opção --experimental-modules é especificada novamente.
  • dependencies: inclui dependências de tempo de execução com o pacote mais recente da API de interatividade.

block.json

O arquivo block.json é o manifesto para o seu bloco do Gutenberg. Ele especifica os metadados, a mídia, os scripts e os estilos a serem carregados. Por padrão, o create-block-interactive-template gera o seguinte block.json:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/your-interactive-block",
	"version": "0.1.0",
	"title": "Your Interactive Block",
	"category": "widgets",
	"icon": "media-interactive",
	"description": "An interactive block with the Interactivity API.",
	"example": {},
	"supports": {
		"interactivity": true
	},
	"textdomain": "your-interactive-block",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"render": "file:./render.php",
	"viewScriptModule": "file:./view.js"
}

Os seguintes campos são essenciais para um bloco interativo:

  • apiVersion: 3 é a versão mais recente da API de blocos e oferece suporte aos recursos de blocos mais recentes, como Script Modules.
  • supports: especifica os recursos compatíveis do bloco. "interactivity": true adiciona suporte à API de interatividade.
  • render: especifica o arquivo PHP responsável pela renderização no frontend. É nesse arquivo que você adiciona as diretivas que tornam o bloco interativo.
  • viewScriptModule: especifica o arquivo JavaScript que contém a lógica de interatividade. Esse arquivo é carregado apenas no frontend e somente se a página contiver o bloco interativo.

render.php

O arquivo render.php é onde você constrói o markup de um bloco dinâmico. Para tornar seu bloco interativo, você precisa adicionar atributos que tornem interativos os elementos DOM do seu bloco.

O arquivo render.php no projeto inicial é semelhante ao seguinte:

<?php
/**
 * PHP file to use when rendering the block type on the server to show on the front end.
 *
 * The following variables are exposed to the file:
 *     $attributes (array): The block attributes.
 *     $content (string): The block default content.
 *     $block (WP_Block): The block instance.
 *
 * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
 */

// Generates a unique id for aria-controls.
$unique_id = wp_unique_id( 'p-' );

// Adds the global state.
wp_interactivity_state(
	'create-block',
	array(
		'isDark'    => false,
		'darkText'  => esc_html__( 'Switch to Light', 'your-interactive-block' ),
		'lightText' => esc_html__( 'Switch to Dark', 'your-interactive-block' ),
		'themeText'	=> esc_html__( 'Switch to Dark', 'your-interactive-block' ),
	)
);
?>

<div
	<?php echo get_block_wrapper_attributes(); ?>
	data-wp-interactive="create-block"
	<?php echo wp_interactivity_data_wp_context( array( 'isOpen' => false ) ); ?>
	data-wp-watch="callbacks.logIsOpen"
	data-wp-class--dark-theme="state.isDark"
>
	<button
		data-wp-on--click="actions.toggleTheme"
		data-wp-text="state.themeText"
	></button>

	<button
		data-wp-on--click="actions.toggleOpen"
		data-wp-bind--aria-expanded="context.isOpen"
		aria-controls="<?php echo esc_attr( $unique_id ); ?>"
	>
		<?php esc_html_e( 'Toggle', 'your-interactive-block' ); ?>
	</button>

	<p
		id="<?php echo esc_attr( $unique_id ); ?>"
		data-wp-bind--hidden="!context.isOpen"
	>
		<?php
			esc_html_e( 'Your Interactive Block - hello from an interactive block!', 'your-interactive-block' );
		?>
	</p>
</div>

Veja o que esse código faz:

  • wp_interactivity_state: obtém e/ou define o estado global inicial de um armazenamento da API de interatividade.
  • data-wp-interactive: habilita a API de interatividade no elemento DOM e em seus elementos internos. Seu valor deve ser o namespace exclusivo do seu plugin ou bloco.
  • wp_interactivity_data_wp_context(): gera a diretiva data-wp-context, que fornece um estado local para um node HTML específico e seus elementos internos.
  • data-wp-watch: executa uma chamada de retorno quando um node é criado e sempre que o estado ou o contexto é alterado.
  • data-wp-class--dark-theme: adiciona ou remove a classe dark-theme ao elemento HTML.
  • data-wp-on--click: executa código de forma síncrona no evento de clique.
  • data-wp-text: define o texto interno do elemento HTML.
  • data-wp-bind--aria-expanded e data-wp-bind--hidden: Define atributos HTML (aria-expanded e hidden) nos elementos correspondentes com base em um valor booleano ou de string.

view.js

Esse arquivo define o Store que contém a lógica e os dados necessários para o comportamento do bloco, incluindo estado, ações e callbacks.

A seguir, você encontra o arquivo view.js gerado pelo projeto inicial:

/**
 * WordPress dependencies
 */
import { store, getContext } from '@wordpress/interactivity';

const { state } = store( 'create-block', {
	state: {
		get themeText() {
			return state.isDark ? state.darkText : state.lightText;
		},
	},
	actions: {
		toggleOpen() {
			const context = getContext();
			context.isOpen = ! context.isOpen;
		},
		toggleTheme() {
			state.isDark = ! state.isDark;
		},
	},
	callbacks: {
		logIsOpen: () => {
			const { isOpen } = getContext();
			// Log the value of `isOpen` each time it changes.
			console.log( `Is open: ${ isOpen }` );
		},
	},
} );
  • store: a função principal usada para criar e registrar o estado global e a lógica do bloco.
  • getContext: função utilizada dentro de ações e callbacks para acessar o estado local (context) do elemento DOM que acionou o evento.
  • state: define os dados reativos globais do bloco.
  • actions: inclui as funções que definem a lógica e modificam o estado.
  • callbacks: contém as funções que são executadas automaticamente em resposta a eventos específicos ou alterações de estado.

É bastante informação, mas não se preocupe, tudo ficará mais claro conforme você avançar pelas próximas seções.

Agora, vamos examinar os principais conceitos da API de interatividade: diretivas, armazenamento, estado, ações e callbacks.

Diretivas da API de interatividade

Como outras bibliotecas de frontend, como Alpine.js e Vue.js, a API de interatividade usa atributos HTML especiais que permitem responder a eventos na página, atualizar o estado do aplicativo, manipular o DOM, aplicar estilos CSS, manipular a entrada do usuário e muito mais.

Esses atributos são chamados de diretivas e permitem que você conecte seu markup à lógica JavaScript subjacente.

Abaixo está uma lista das diretivas que você mais usará.

Função Diretiva Descrição
Ativação/Namespace data-wp-interactive Ativa a API para o elemento e seus elementos internos. O valor deve ser o identificador exclusivo do seu plugin.
Estado local data-wp-context Fornece um estado local (“contexto”) para o elemento atual e todos os elementos internos. Ele aceita um objeto JSON. Observe que é recomendável que você use wp_interactivity_data_wp_context() para defini-lo no PHP (normalmente render.php).
Vinculação de atributo data-wp-bind--[attribute] Define um atributo HTML (por exemplo, disabled, value) com base em um estado reativo ou valor de contexto (um valor booleano ou ou string).
Modificação de texto data-wp-text Define o conteúdo de texto interno do elemento. Aceita apenas strings.
Alternância de classe CSS data-wp-class--[classname] Adiciona ou remove uma propriedade CSS inline com base em um valor booleano.
Estilo inline data-wp-style--[css-property] Adiciona ou remove uma classe de estilo em linha, dependendo de um valor booleano.
Manipulação de eventos data-wp-on--[event] Executa código em resposta a eventos DOM padrão, como click ou mouseover.
Execução inicial data-wp-init Executa uma função de callback apenas uma vez, quando o node é criado.
Observação de estado data-wp-watch Executa um callback quando o node é criado e novamente sempre que o estado ou contexto muda.
Iteração de lista data-wp-each Renderiza uma lista de elementos.

Para obter uma lista completa de diretivas, consulte as notas de desenvolvimento da API de interatividade e a referência da API.

Estado global, contexto local e estado derivado

Antes de começar a usar a API de interatividade, é essencial que você se familiarize com os conceitos fundamentais de gerenciamento de estado no desenvolvimento de frontend. Aqueles que desenvolvem regularmente com React, Vue ou Angular já estarão familiarizados com esses conceitos. Para aqueles que são novos nessas tecnologias, pode ser útil fornecer algumas definições gerais.

Estado global

O estado global refere-se ao conjunto de dados acessíveis a partir de quase todos os componentes de um aplicativo. No caso da API de interatividade, por exemplo, o estado global afeta todos os blocos interativos da página, mantendo-os sincronizados. Por exemplo, quando um usuário adiciona um produto à sua cesta, isso é refletido no bloco do carrinho de compras.

Ao usar a API de interatividade, você deve definir os valores iniciais do estado global no servidor usando a função wp_interactivity_state(). No projeto inicial descrito anteriormente, essa função é usada no arquivo render.php da seguinte forma:

// Adds the global state.
wp_interactivity_state(
	'create-block',
	array(
		'isDark'    => false,
		'darkText'  => esc_html__( 'Switch to Light', 'your-interactive-block' ),
		'lightText' => esc_html__( 'Switch to Dark', 'your-interactive-block' ),
		'themeText'	=> esc_html__( 'Switch to Dark', 'your-interactive-block' ),
	)
);

Essa função aceita dois argumentos:

  • Um identificador exclusivo para o namespace da loja. Nesse caso, create-block.
  • Uma array de dados que será mesclada com o namespace da loja existente, se existir.

Os valores iniciais do estado global são usados para renderizar a página. Você pode acessar os valores do estado global diretamente usando state nos valores das diretivas, como neste exemplo:

<button
	data-wp-on--click="actions.toggleTheme"
	data-wp-text="state.themeText"
></button>

A função store() fornece o principal ponto de acesso ao estado global do JavaScript, limitado ao namespace selecionado. Voltando ao código do projeto inicial, a função store() é usada no arquivo view.js da seguinte forma:

import { store, getContext } from '@wordpress/interactivity';

const { state } = store( 'create-block', {
	state: { ... },
	actions: { ... },
	callbacks: { ... },
} );

Para acessar o estado global, você pode usar a propriedade state:

actions: {
	toggleTheme() {
		state.isDark = ! state.isDark;
	},
},

Contexto local

O contexto local são dados que só podem ser acessados por um componente específico e seus elementos internos diretos. Um bloco interativo do WordPress fornece um estado independente para o bloco e seus elementos aninhados.

Ao usar a API de interatividade, você pode acessar o contexto local usando a função getContext(). Referindo-se novamente ao projeto inicial, quando o usuário clica no botão Toggle, a ação toggleOpen() é acionada, acessando o contexto Local do componente:

actions: {
	toggleOpen() {
		const context = getContext();
		context.isOpen = ! context.isOpen;
	},
},
  • getContext(): recupera o objeto de estado local do bloco. As propriedades desse objeto são definidas no markup do componente (render.php) usando a função wp_interactivity_data_wp_context().
  • context.isOpen = ! context.isOpen;: alterna o valor da propriedade isOpen no contexto local do componente.

Estado derivado

O estado derivado refere-se a dados calculados dinamicamente a partir do estado global ou local existente.

Por exemplo, dê uma olhada no código do arquivo view.js, especificamente nesta seção:

const { state } = store( 'create-block', {
	state: {
		get themeText() {
			return state.isDark ? state.darkText : state.lightText;
		},
	},
	...
}

Esse bloco define o estado derivado themeText dentro do estado global definido no namespace create-block.

  • get themeText() não é um valor fixo, mas sim uma função que é executada sempre que você tenta ler a propriedade themeText. Ela não deve ser chamada como uma função normal porque a API de interatividade a trata como uma propriedade de estado e recalcula automaticamente seu valor sempre que os valores de outras propriedades de estado são alterados. No código acima, o valor da propriedade themeText é recalculado sempre que o valor da propriedade isDark é alterado. Se state.isDark for true, então themeText assumirá o valor de state.darkText; caso contrário, assumirá o valor de state.lightText.

Para uma explicação mais completa desses conceitos, consulte Entendendo o estado global, o contexto local e o estado derivado.

Ações e callbacks

As ações e callbacks determinam a resposta à interação do usuário e às alterações de estado.

A seção actions de um bloco interativo contém funções que são executadas em resposta a eventos gerados pelo usuário. Essas funções servem principalmente para modificar o estado local ou global do componente. Veja o exemplo a seguir retirado do arquivo view.js:

actions: {
	toggleOpen() {
		const context = getContext();
		context.isOpen = ! context.isOpen;
	},
	...
},
  • Nessa seção do código, a função toggleOpen() usa getContext() para acessar o contexto Local do bloco que acionou a ação para mudar o valor da propriedade isOpen.

Da mesma forma, você pode acessar o estado Global:

actions: {
	...,
	toggleTheme() {
		state.isDark = ! state.isDark;
	},
},
  • A função toggleTheme() acessa diretamente o objeto global state e altera o valor da propriedade isDark.

As ações são acionadas por meio da diretiva data-wp-on--[event]. Por exemplo, no arquivo render.php, você encontrará o seguinte botão:

<button
	data-wp-on--click="actions.toggleOpen"
	data-wp-bind--aria-expanded="context.isOpen"
	aria-controls="<?php echo esc_attr( $unique_id ); ?>"
>
  • Nesse código HTML, o atributo data-wp-on--click ativa a ação toggleOpen quando o usuário clica no botão de alternância.

A seção callbacks contém funções que são executadas automaticamente quando os dados dos quais elas dependem são alterados. Seu objetivo é produzir efeitos colaterais em resposta a uma alteração de estado.

No projeto básico gerado por create-block-interactive-template, você encontrará o seguinte callback:

callbacks: {
	logIsOpen: () => {
		const { isOpen } = getContext();
		// Log the value of `isOpen` each time it changes.
		console.log( `Is open: ${ isOpen }` );
	},
},
  • A função logIsOpen usa a variável isOpen, que está disponível no contexto Local.
  • O callback recupera o valor de isOpen usando getContext().
  • Sempre que o valor de isOpen é alterado, a função lança uma mensagem no console do navegador.
Uma mensagem no console informa o usuário sobre a alteração no contexto Local.
Uma mensagem no console informa o usuário sobre a alteração no contexto Local.

Como criar um bloco interativo

Agora que abordamos a teoria, é hora de colocar o código em prática. Na segunda parte deste guia, você aprenderá a criar um bloco interativo que permite aos usuários adicionar produtos a uma cesta de compras ideal, com quantidades e totais atualizados automaticamente. Este é um exemplo demonstrativo, mas deve ajudar a compreender claramente como usar state, actions e callbacks.

O bloco interativo no editor.
O bloco interativo no editor.

Criaremos um bloco chamado Interactive Counter usando o create-block-interactive-template. Para começar, abra a ferramenta de linha de comando e digite o seguinte:

npx @wordpress/create-block interactive-counter --template @wordpress/create-block-interactive-template

Em seguida, navegue até o diretório do novo projeto e execute o primeiro build:

cd interactive-counter && npm run build

Abra o projeto em seu editor de código agora. No diretório /src, procure o arquivo block.json. Ele deve se parecer com o exemplo abaixo:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/interactive-counter",
	"version": "0.1.0",
	"title": "Interactive Counter",
	"category": "widgets",
	"icon": "media-interactive",
	"description": "An interactive block with the Interactivity API.",
	"supports": {
		"interactivity": true
	},
	"textdomain": "interactive-counter",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"render": "file:./render.php",
	"viewScriptModule": "file:./view.js"
}

Você pode personalizar o arquivo à vontade, mas certifique-se de não modificar os campos essenciais descritos acima, pois eles são necessários para o funcionamento do bloco interativo.

O arquivo edit.js

A próxima etapa é criar o bloco que será exibido no editor. Para fazer isso, você precisará editar o arquivo /src/edit.js. Abra o arquivo e modifique-o da seguinte forma:

import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import './editor.scss';

export default function Edit({ attributes, setAttributes }) {
	const blockProps = useBlockProps();
	const products = [
		{ id: 'product1', name: __('Product 1', 'interactive-counter'), price: 10.00 },
		{ id: 'product2', name: __('Product 2', 'interactive-counter'), price: 15.00 },
		{ id: 'product3', name: __('Product 3', 'interactive-counter'), price: 20.00 },
	];

	return (
		<div {...blockProps}>
			<h3>{__('Shopping Cart', 'interactive-counter')}</h3>
			<ul>
				{products.map((product) => (
					<li key={product.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
						<span style={{ flex: 1 }}>{product.name} - ${product.price.toFixed(2)}</span>
						<div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
							<button disabled>-</button>
							<span>0</span>
							<button disabled>+</button>
						</div>
						<span style={{ flex: 1, textAlign: 'right' }}>
							{__('Subtotal:', 'interactive-counter')} $0.00
						</span>
					</li>
				))}
			</ul>
			<div style={{ borderTop: '1px solid #ccc', paddingTop: '15px' }}>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Subtotal:', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Tax (22%):', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
				<p style={{ display: 'flex', justifyContent: 'space-between' }}>
					<strong>{__('Total:', 'interactive-counter')}</strong>
					<span>$0.00</span>
				</p>
			</div>
			<p>{__('Quantities and totals will be interactive in the frontend.', 'interactive-counter')}</p>
		</div>
	);
}

Esse código gera um bloco personalizado no backend. O bloco será interativo somente no frontend. Para obter mais detalhes sobre o arquivo /src/edit.js, consulte nossos guias de desenvolvimento de blocos do Gutenberg.

O arquivo render.php

O próximo arquivo a ser editado é /src/render.php. Abra o arquivo e substitua o código existente pelo seguinte:

<?php
/**
 * Render callback for the interactive-counter block.
 */

$products = [
	['id' => 'product1', 'name' => __('Product 1', 'interactive-counter'), 'price' => 10.00],
	['id' => 'product2', 'name' => __('Product 2', 'interactive-counter'), 'price' => 15.00],
	['id' => 'product3', 'name' => __('Product 3', 'interactive-counter'), 'price' => 20.00],
];

// Initialize global state
wp_interactivity_state('interactive-counter', [
	'products' => array_map(function ($product) {
		return [
			'id' => $product['id'],
			'name' => $product['name'],
			'price' => $product['price'],
			'quantity' => 0,
			'subtotal' => '0.00',
		];
	}, $products),
	'vatRate' => 0.22,
]);

Veja o que esse código faz:

  • Primeiro, cria um array fixo (hard-coded) de produtos. Cada produto tem um ID, um nome e um preço.
  • Em seguida, inicializa o estado Global com wp_interactivity_state. O primeiro parâmetro é o nome do store, que deve corresponder ao usado em view.js.
  • Depois, mapeia o array anterior de produtos para um novo array products, adicionando quantity e subtotal às propriedades do array original. Esse novo array fornece a estrutura de dados que você usará em view.js.
  • vatRate define o valor padrão para o cálculo de impostos.

Em seguida, adicione o seguinte ao código acima:

<div <?php echo get_block_wrapper_attributes(); ?> data-wp-interactive="interactive-counter" data-wp-init="callbacks.init">
	<h3><?php echo esc_html__('Cart', 'interactive-counter'); ?></h3>
	<ul>
		<?php foreach ($products as $index => $product) : ?>
			<li data-wp-context='{
				"productId": "<?php echo esc_attr($product['id']); ?>",
				"quantity": 0,
				"subtotal": "0.00"
			}' 
			data-wp-bind--data-wp-context.quantity="state.products[<?php echo $index; ?>].quantity" 
			data-wp-bind--data-wp-context.subtotal="state.products[<?php echo $index; ?>].subtotal">
				<span class="product-name"><?php echo esc_html($product['name']); ?> - $<?php echo esc_html(number_format($product['price'], 2)); ?></span>
				<div class="quantity-controls">
					<button data-wp-on--click="actions.decrement">-</button>
					<span data-wp-text="context.quantity">0</span>
					<button data-wp-on--click="actions.increment">+</button>
				</div>
				<span class="product-subtotal">
					<?php echo esc_html__('Subtotal:', 'interactive-counter'); ?>
					$<span data-wp-text="context.subtotal">0.00</span>
				</span>
			</li>
		<?php endforeach; ?>
	</ul>
	<div class="totals">
		<p>
			<strong><?php echo esc_html__('Subtotal:', 'interactive-counter'); ?></strong>
			$ <span data-wp-text="state.subtotal">0.00</span>
		</p>
		<p>
			<strong><?php echo esc_html__('Tax (22%):', 'interactive-counter'); ?></strong>
			$ <span data-wp-text="state.vat">0.00</span>
		</p>
		<p>
			<strong><?php echo esc_html__('Total:', 'interactive-counter'); ?></strong>
			$ <span data-wp-text="state.total">0.00</span>
		</p>
	</div>
</div>

Veja o que esse código faz:

  • A função get_block_wrapper_attributes() na div contêiner é uma função do WordPress que gera os atributos padrão de um bloco. Neste caso, ela gera o atributo class "wp-block-create-block-interactive-counter".
  • O atributo data-wp-interactive torna este bloco interativo.
  • O atributo data-wp-init aciona o callback init definido em view.js.
  • O foreach gera um item de lista para cada produto no array products.
  • data-wp-context define o contexto Local do bloco.
  • data-wp-bind vincula o valor de data-wp-context.quantity à propriedade state.products[$index].quantity do estado global.
  • O mesmo ocorre na linha abaixo com o subtotal.
  • Os dois botões seguintes ativam as ações decrement e increment graças ao atributo data-wp-on--click.
  • O atributo data-wp-text no span atualiza o conteúdo do elemento com base no valor atual de context.quantity

O restante do código é autoexplicativo; vamos avançar para o próximo arquivo.

O arquivo view.js

Esse arquivo contém a lógica do seu bloco interativo.

import { store, getContext } from '@wordpress/interactivity';

store('interactive-counter', {
	state: {
		get subtotal() {
			const { products } = store('interactive-counter').state;
			return products
				.reduce((sum, product) => sum + product.price * (product.quantity || 0), 0)
				.toFixed(2);
		},
		get vat() {
			const { subtotal, vatRate } = store('interactive-counter').state;
			return (subtotal * vatRate).toFixed(2);
		},
		get total() {
			const { subtotal, vat } = store('interactive-counter').state;
			return (parseFloat(subtotal) + parseFloat(vat)).toFixed(2);
		},
	},
	actions: {
		increment: () => {
			const context = getContext();
			const { products } = store('interactive-counter').state;
			const product = products.find(p => p.id === context.productId);
			if (product) {
				product.quantity = (product.quantity || 0) + 1;
				product.subtotal = (product.price * product.quantity).toFixed(2);
				context.quantity = product.quantity;
				context.subtotal = product.subtotal;
				console.log(`Incremented ${context.productId}:`, { quantity: product.quantity, subtotal: product.subtotal, context });
			} else {
				console.warn('Product not found:', context.productId);
			}
		},
		decrement: () => {
			const context = getContext();
			const { products } = store('interactive-counter').state;
			const product = products.find(p => p.id === context.productId);
			if (product && (product.quantity || 0) > 0) {
				product.quantity -= 1;
				product.subtotal = (product.price * product.quantity).toFixed(2);
				context.quantity = product.quantity;
				context.subtotal = product.subtotal;
				console.log(`Decremented ${context.productId}:`, { quantity: product.quantity, subtotal: product.subtotal, context });
			} else {
				console.warn('Cannot decrement:', context.productId, product?.quantity);
			}
		},
	},
	callbacks: {
		init: () => {
			const { products } = store('interactive-counter').state;
			products.forEach((product, index) => {
				product.quantity = 0;
				product.subtotal = '0.00';
				console.log(`Initialized product ${index}:`, { id: product.id, quantity: product.quantity, subtotal: product.subtotal });
			});
		},
	},
});

Esse arquivo define o armazenamento para o namespace interactive-counter. Ele gerencia o estado, as ações e callbacks:

store('interactive-counter', {
	state: { ... },
	actions: { ... },
	callbacks: { ... },
});

Vamos dar uma olhada mais de perto.

  • state: define três propriedades de estado computadas (getters): subtotal, vat e total. Essas funções recuperam valores do estado Global e calculam os valores a serem retornados.
  • actions: define duas funções executadas em eventos: increment e decrement. Essas funções obtêm o array products do estado Global, recuperam o produto atual do contexto Local com base em context.productId, atualizam os valores das propriedades do produto atual (quantity e subtotal) e sincronizam o contexto Local com os novos valores.
  • callbacks: define um callback init para inicialização.

A imagem a seguir mostra o bloco interativo no frontend.

Um contador interativo criado com a API de interatividade.
Um contador interativo criado com a API de interatividade.

Resumo

Neste artigo, apresentamos os principais recursos da API de interatividade do WordPress. Exploramos conceitos fundamentais como estado global, contexto local, diretivas, ações e callbacks. Você aprendeu a criar um bloco interativo do zero usando o @wordpress/create-block-interactive-template e colocamos isso em prática criando um bloco real que interage com as ações do usuário.

Esperamos ter fornecido as ferramentas e o conhecimento necessários para que você crie sites WordPress fantásticos, dinâmicos e interativos usando a Interactivity API.

Carlo Daniele Kinsta

Carlo é um apaixonado por webdesign e desenvolvimento frontend. Ele tem mais de 10 anos de experiência com WordPress e colaborou com diversas universidades e instituições educacionais na Itália e na Europa. Carlo já publicou inúmeros artigos e guias sobre WordPress, tanto em sites italianos quanto internacionais, além de revistas impressas. Você pode seguir ele no LinkedIn e no X.