Esta é uma continuação do meu último artigo sobre “Renderizando dados de API externos em blocos do WordPress no front-end”. Nesse último, aprendemos como pegar uma API externa e integrá-la a um bloco que renderiza os dados buscados no front-end de um site WordPress.
O problema é que conseguimos isso de uma maneira que nos impede de ver os dados no WordPress Block Editor. Em outras palavras, podemos inserir o bloco em uma página, mas não obtemos uma prévia dele. Nós só conseguimos ver o bloco quando ele é publicado.
Vamos revisitar o plug-in de bloco de exemplo que fizemos no último artigo. Só que desta vez, vamos usar o ecossistema JavaScript e React do WordPress para buscar e renderizar esses dados também no Editor de Blocos de back-end.
De onde paramos
Ao iniciarmos isso, aqui está uma demonstração onde chegamos no último artigo que você pode fazer referência. Você deve ter notado que eu usei um render_callback
no último artigo para que eu possa usar os atributos no arquivo PHP e renderizar o conteúdo.
Bem, isso pode ser útil em situações em que você pode ter que usar alguma função nativa do WordPress ou PHP para criar blocos dinâmicos. Mas se você quiser usar apenas o ecossistema JavaScript e React (JSX, especificamente) do WordPress para renderizar o HTML estático junto com os atributos armazenados no banco de dados, você só precisa se concentrar no Edit
e Save
funções do plugin de bloco.
- A
Edit
A função renderiza o conteúdo com base no que você deseja ver no Editor de blocos. Você pode ter componentes interativos do React aqui. - A
Save
A função renderiza o conteúdo com base no que você deseja ver no front-end. Você não pode ter os componentes regulares do React ou os ganchos aqui. Ele é usado para retornar o HTML estático que é salvo em seu banco de dados junto com os atributos.
A Save
função é onde estamos saindo hoje. Podemos criar componentes interativos no front-end, mas para isso precisamos incluí-los manualmente e acessá-los fora do Save
função em um arquivo como fizemos no último artigo.
Então, vou cobrir o mesmo terreno que fizemos no artigo anterior, mas desta vez você pode ver a visualização no Editor de Blocos antes você publicá-lo no front-end.
Os suportes do bloco
Eu intencionalmente deixei de fora quaisquer explicações sobre o edit
adereços da função no último artigo porque isso teria tirado o foco do ponto principal, a renderização.
Se você tem experiência em React, provavelmente entenderá do que estou falando, mas se você é novo nisso, recomendo verificando componentes e adereços na documentação do React.
Se registrarmos o props
object para o console, ele retorna uma lista de funções e variáveis do WordPress relacionadas ao nosso bloco:
Precisamos apenas do attributes
objeto e o setAttributes
função que vou desestruturar do props
objeto no meu código. No último artigo, modifiquei o código do RapidAPI para poder armazenar os dados da API por meio de setAttributes()
. Os adereços são apenas legíveis, por isso não podemos modificá-los diretamente.
Adereços de bloco são semelhantes a variáveis de estado e setState
no React, mas o React funciona no lado do cliente e setAttributes()
é usado para armazenar os atributos permanentemente no banco de dados do WordPress após salvar a postagem. Então, o que precisamos fazer é salvá-los para attributes.data
e, em seguida, chame isso como o valor inicial para o useState()
variável.
edit
função
A Vou copiar e colar o código HTML que usamos no football-rankings.php
no último artigo e edite-o um pouco para mudar para o plano de fundo do JavaScript. Lembra como criamos dois arquivos adicionais no último artigo para o estilo e os scripts do front-end? Com a maneira como estamos abordando as coisas hoje, não há necessidade de criar esses arquivos. Em vez disso, podemos mover tudo para o Edit
função.
Código completo
import { useState } from "@wordpress/element";
export default function Edit(props) {
const { attributes, setAttributes } = props;
const [apiData, setApiData] = useState(null);
function fetchData() {
const options = {
method: "GET",
headers: {
"X-RapidAPI-Key": "Your Rapid API key",
"X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
},
};
fetch(
"https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39",
options
)
.then((response) => response.json())
.then((response) => {
let newData = { ...response }; // Deep clone the response data
setAttributes({ data: newData }); // Store the data in WordPress attributes
setApiData(newData); // Modify the state with the new data
})
.catch((err) => console.error(err));
}
return (
{apiData && (
Rank
Logo
Team name
GP
GW
GD
GL
GF
GA
Pts
Form history
{/* Usage of [0] might be weird but that is how the API structure is. */}
{apiData.response[0].league.standings[0].map((el) => {
{/* Destructure the required data from all */}
const { played, win, draw, lose, goals } = el.all;
return (
{el.rank}
{el.team.name}
{played}
{win}
{draw}
{lose}
{goals.for}
{goals.against}
{el.points}
{el.form.split("").map((result) => {
return (
{result}
);
})}
);
}
)}
)}
);
}
Eu incluí o gancho React useState()
da @wordpress/element
em vez de usá-lo da biblioteca React. Isso porque se eu carregasse da maneira normal, ele baixaria o React para cada bloco que estou usando. Mas se estou usando @wordpress/element
ele carrega de uma única fonte, ou seja, a camada do WordPress em cima do React.
Desta vez, também não embrulhei o código dentro useEffect()
mas dentro de uma função que é chamada apenas ao clicar em um botão para que tenhamos uma visualização ao vivo dos dados buscados. Eu usei uma variável de estado chamada apiData
para renderizar a tabela de classificação condicionalmente. Então, uma vez que o botão é clicado e os dados são buscados, estou configurando apiData
para os novos dados dentro do fetchData()
e há uma rerenderização com o HTML da tabela de classificação de futebol disponível.
Você notará que assim que a postagem for salva e a página for atualizada, a tabela de classificação desaparecerá. Isso porque estamos usando um estado vazio (null
) Para apiData
valor inicial de. Quando a postagem é salva, os atributos são salvos no attributes.data
objeto e nós o chamamos como o valor inicial para o useState()
variável como esta:
const [apiData, setApiData] = useState(attributes.data);
save
função
A Vamos fazer quase exatamente a mesma coisa com o save
função, mas modifique-o um pouco. Por exemplo, não há necessidade do botão "Buscar dados" no front end, e o apiData
variável de estado também é desnecessária porque já estamos verificando no edit
função. Mas precisamos de um aleatório apiData
variável que verifica attributes.data
para renderizar condicionalmente o JSX ou então ele lançará erros indefinidos e a interface do usuário do Block Editor ficará em branco.
Código completo
export default function save(props) {
const { attributes, setAttributes } = props;
let apiData = attributes.data;
return (
{/* Only render if apiData is available */}
{apiData && (
Rank
Logo
Team name
GP
GW
GD
GL
GF
GA
Pts
Form history
{/* Usage of [0] might be weird but that is how the API structure is. */}
{apiData.response[0].league.standings[0].map((el) => {
const { played, win, draw, lose, goals } = el.all;
return (
{el.rank}
{el.team.name}
{played}
{win}
{draw}
{lose}
{goals.for}
{goals.against}
{el.points}
{el.form.split("").map((result) => {
return (
{result}
);
})}
);
})}
)}
);
}
Se você estiver modificando o save
função após um bloco já estar presente no Editor de Blocos, ele mostraria um erro como este:
Isso ocorre porque a marcação no conteúdo salvo é diferente da marcação em nosso novo save
função. Como estamos no modo de desenvolvimento, é mais fácil remover o bock da página atual e reinseri-lo como um novo bloco — dessa forma, o código atualizado é usado e as coisas voltam a sincronizar.
Esta situação de removê-lo e adicioná-lo novamente pode ser evitada se tivéssemos usado o render_callback
já que a saída é dinâmica e controlada pelo PHP em vez da função save. Portanto, cada método tem suas próprias vantagens e desvantagens.
Tom Nowell fornece uma explicação completa sobre o que não fazer em um save
funcionam em este estouro de pilha responder.
Estilizando o bloco no editor e no front end
Em relação ao estilo, será quase a mesma coisa que vimos no artigo anterior, mas com algumas pequenas alterações que expliquei nos comentários. Estou apenas fornecendo os estilos completos aqui, já que isso é apenas uma prova de conceito, em vez de algo que você deseja copiar e colar (a menos que você realmente precise de um bloco para mostrar os rankings de futebol com esse estilo). E observe que ainda estou usando o SCSS que compila para CSS na compilação.
Estilos de editor
/* Target all the blocks with the data-title="Football Rankings" */
.block-editor-block-list__layout
.block-editor-block-list__block.wp-block[data-title="Football Rankings"] {
/* By default, the blocks are constrained within 650px max-width plus other design specific code */
max-width: unset;
background: linear-gradient(to right, #8f94fb, #4e54c8);
display: grid;
place-items: center;
padding: 60px 0;
/* Button CSS - From: https://getcssscan.com/css-buttons-examples - Some properties really not needed :) */
button.fetch-data {
align-items: center;
background-color: #ffffff;
border: 1px solid rgb(0 0 0 / 0.1);
border-radius: 0.25rem;
box-shadow: rgb(0 0 0 / 0.02) 0 1px 3px 0;
box-sizing: border-box;
color: rgb(0 0 0 / 0.85);
cursor: pointer;
display: inline-flex;
font-family: system-ui, -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 16px;
font-weight: 600;
justify-content: center;
line-height: 1.25;
margin: 0;
min-height: 3rem;
padding: calc(0.875rem - 1px) calc(1.5rem - 1px);
position: relative;
text-decoration: none;
transition: all 250ms;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
vertical-align: baseline;
width: auto;
&:hover,
&:focus {
border-color: rgb(0, 0, 0, 0.15);
box-shadow: rgb(0 0 0 / 0.1) 0 4px 12px;
color: rgb(0, 0, 0, 0.65);
}
&:hover {
transform: translateY(-1px);
}
&:active {
background-color: #f0f0f1;
border-color: rgb(0 0 0 / 0.15);
box-shadow: rgb(0 0 0 / 0.06) 0 2px 4px;
color: rgb(0 0 0 / 0.65);
transform: translateY(0);
}
}
}
Estilos de front-end
/* Front-end block styles */
.wp-block-post-content .wp-block-football-rankings-league-table {
background: linear-gradient(to right, #8f94fb, #4e54c8);
max-width: unset;
display: grid;
place-items: center;
}
#league-standings {
width: 900px;
margin: 60px 0;
max-width: unset;
font-size: 16px;
.header {
display: grid;
gap: 1em;
padding: 10px;
grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
align-items: center;
color: white;
font-size: 16px;
font-weight: 600;
background-color: transparent;
background-repeat: no-repeat;
background-size: contain;
background-position: right;
.stats {
display: flex;
gap: 15px;
& > div {
width: 30px;
}
}
}
}
.league-table {
background: white;
box-shadow:
rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
padding: 1em;
.position {
width: 20px;
}
.team {
display: grid;
gap: 1em;
padding: 10px 0;
grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
align-items: center;
}
.team:not(:last-child) {
border-bottom: 1px solid lightgray;
}
.team-logo img {
width: 30px;
top: 3px;
position: relative;
}
.stats {
display: flex;
gap: 15px;
& > div {
width: 30px;
text-align: center;
}
}
.last-5-games {
display: flex;
gap: 5px;
& > div {
width: 25px;
height: 25px;
text-align: center;
border-radius: 3px;
font-size: 15px;
& .result-W {
background: #347d39;
color: white;
}
& .result-D {
background: gray;
color: white;
}
& .result-L {
background: lightcoral;
color: white;
}
}
}
Nós adicionamos isso a src/style.scss
que cuida do estilo no editor e no frontend. Não poderei compartilhar o URL de demonstração, pois exigiria acesso de editor, mas tenho um vídeo gravado para você ver a demonstração:
Bem legal, certo? Agora temos um bloco totalmente funcional que não apenas renderiza no front-end, mas também busca dados da API e renderiza ali mesmo no Editor de Blocos — com um botão de atualização para inicializar!
Mas se quisermos tomar cheio vantagem do WordPress Block Editor, devemos considerar mapear alguns dos elementos da interface do usuário do bloco para controles de bloco para coisas como definir cor, tipografia e espaçamento. Esse é um bom próximo passo na jornada de aprendizado do desenvolvimento de blocos.