To jest kontynuacja mojego ostatniego artykułu o „Renderowanie zewnętrznych danych API w blokach WordPress na interfejsie”. W tym ostatnim nauczyliśmy się, jak wziąć zewnętrzny interfejs API i zintegrować go z blokiem, który renderuje pobrane dane na interfejsie witryny WordPress.
Chodzi o to, że osiągnęliśmy to w sposób, który uniemożliwia nam zobaczenie danych w edytorze bloków WordPress. Innymi słowy, możemy wstawić blok na stronie, ale nie otrzymujemy jego podglądu. Widzimy blok tylko wtedy, gdy jest opublikowany.
Wróćmy do przykładowej wtyczki bloku, którą stworzyliśmy w poprzednim artykule. Tylko tym razem użyjemy ekosystemu JavaScript i React WordPressa, aby pobrać i wyrenderować te dane również w edytorze bloków zaplecza.
Gdzie skończyliśmy
Kiedy to zaczynamy, oto demo gdzie wylądowaliśmy w ostatnim artykule, do którego możesz się odwołać. Być może zauważyłeś, że użyłem render_callback
w poprzednim artykule, abym mógł skorzystać z atrybutów w pliku PHP i renderować zawartość.
Cóż, może to być przydatne w sytuacjach, w których będziesz musiał użyć natywnej funkcji WordPress lub PHP do tworzenia bloków dynamicznych. Ale jeśli chcesz skorzystać tylko z ekosystemu JavaScript i React (w szczególności JSX) WordPressa do renderowania statycznego kodu HTML wraz z atrybutami przechowywanymi w bazie danych, musisz tylko skupić się na Edit
i Save
funkcje wtyczki bloku.
- Połączenia
Edit
Funkcja renderuje zawartość na podstawie tego, co chcesz zobaczyć w Edytorze bloku. Tutaj możesz mieć interaktywne komponenty React. - Połączenia
Save
funkcja renderuje zawartość na podstawie tego, co chcesz zobaczyć na interfejsie. Nie możesz mieć tutaj zwykłych komponentów Reacta ani haków. Służy do zwracania statycznego kodu HTML zapisanego w bazie danych wraz z atrybutami.
Połączenia Save
funkcja to miejsce, w którym dzisiaj spędzamy czas. Możemy tworzyć interaktywne komponenty na interfejsie użytkownika, ale w tym celu musimy ręcznie dołączyć i uzyskać do nich dostęp poza Save
funkcji w pliku, tak jak to zrobiliśmy w poprzednim artykule.
Zamierzam więc omówić ten sam temat, co w poprzednim artykule, ale tym razem możesz zobaczyć podgląd w Edytorze bloku zanim publikujesz go w interfejsie użytkownika.
Rekwizyty blokowe
Celowo pominąłem wszelkie wyjaśnienia dotyczące edit
właściwości funkcji w poprzednim artykule, ponieważ odciągnęłoby to uwagę od głównego punktu, czyli renderowania.
Jeśli pochodzisz ze środowiska React, prawdopodobnie zrozumiesz, o czym mówię, ale jeśli jesteś w tym nowy, polecam sprawdzanie komponentów i rekwizytów w dokumentacji React.
Jeśli zarejestrujemy props
obiektu do konsoli, zwraca listę funkcji WordPressa i zmiennych związanych z naszym blokiem:
Potrzebujemy tylko attributes
obiekt i setAttributes
funkcji, którą zamierzam zdestrukturyzować z props
obiekt w moim kodzie. W ostatnim artykule zmodyfikowałem kod RapidAPI, abym mógł przechowywać dane API poprzez setAttributes()
. Rekwizyty są tylko czytelne, więc nie możemy ich bezpośrednio modyfikować.
Rekwizyty blokowe są podobne do zmiennych stanu i setState
w React, ale React działa po stronie klienta i setAttributes()
służy do trwałego przechowywania atrybutów w bazie danych WordPressa po zapisaniu posta. Więc to, co musimy zrobić, to zapisać je, aby attributes.data
a następnie nazwij to jako wartość początkową dla useState()
zmienna.
edit
funkcjonować
Połączenia Skopiuję i wkleję kod HTML, którego użyliśmy w football-rankings.php
w ostatnim artykule i edytuj go trochę, aby przejść do tła JavaScript. Pamiętasz, jak w poprzednim artykule stworzyliśmy dwa dodatkowe pliki do stylizacji i skryptów frontendu? Przy obecnym sposobie podejścia do rzeczy nie ma potrzeby tworzenia tych plików. Zamiast tego możemy przenieść to wszystko do Edit
funkcja.
Pełny kod
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}
);
})}
);
}
)}
)}
);
}
Dołączyłem hak React useState()
od @wordpress/element
zamiast używać go z biblioteki React. Dzieje się tak dlatego, że gdybym miał załadować zwykły sposób, pobrałby React dla każdego bloku, którego używam. Ale jeśli używam @wordpress/element
ładuje się z jednego źródła, tj. warstwy WordPress na wierzchu React.
Tym razem też nie zapakowałem kodu do środka useEffect()
ale wewnątrz funkcji, która jest wywoływana tylko po kliknięciu przycisku, dzięki czemu mamy podgląd na żywo pobranych danych. Użyłem zmiennej stanu o nazwie apiData
do warunkowego renderowania tabeli ligowej. Tak więc po kliknięciu przycisku i pobraniu danych ustawiam apiData
do nowych danych wewnątrz fetchData()
i dostępny jest render z kodem HTML tabeli rankingów piłkarskich.
Zauważysz, że po zapisaniu wpisu i odświeżeniu strony, tabela ligowa zniknie. Dzieje się tak, ponieważ używamy pustego stanu (null
) Dla apiData
wartość początkowa. Po zapisaniu wpisu atrybuty są zapisywane w attributes.data
obiekt i nazywamy go jako wartość początkową dla useState()
zmienna w ten sposób:
const [apiData, setApiData] = useState(attributes.data);
save
funkcjonować
Połączenia Zrobimy prawie to samo z save
funkcji, ale trochę ją zmodyfikuj. Na przykład nie ma potrzeby używania przycisku „Pobierz dane” w interfejsie, a apiData
zmienna state jest również niepotrzebna, ponieważ sprawdzamy ją już w edit
funkcjonować. Ale potrzebujemy losowego apiData
zmienna, która sprawdza attributes.data
do warunkowego renderowania JSX, w przeciwnym razie wygeneruje niezdefiniowane błędy, a interfejs Edytora bloków będzie pusty.
Pełny kod
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}
);
})}
);
})}
)}
);
}
Jeśli modyfikujesz save
funkcja po tym, jak blok jest już obecny w Edytorze bloku, wyświetli błąd taki:
Dzieje się tak, ponieważ znaczniki w zapisanej treści różnią się od znaczników w naszym nowym save
funkcjonować. Ponieważ jesteśmy w trybie programistycznym, łatwiej jest usunąć blokadę z bieżącej strony i wstawić ją ponownie jako nowy blok — w ten sposób zamiast tego zostanie użyty zaktualizowany kod i wszystko będzie zsynchronizowane.
Tej sytuacji usunięcia go i ponownego dodania można uniknąć, gdybyśmy użyli render_callback
metoda, ponieważ wyjście jest dynamiczne i kontrolowane przez PHP zamiast funkcji save. Tak więc każda metoda ma swoje zalety i wady.
Tom Nowell dokładnie wyjaśnia, czego nie robić w save
funkcja w ten przepełnienie stosu odpowiedź.
Stylizowanie bloku w edytorze i interfejsie
Jeśli chodzi o stylizację, będzie to prawie to samo, o czym mówiliśmy w poprzednim artykule, ale z drobnymi zmianami, które wyjaśniłem w komentarzach. Po prostu dostarczam tutaj pełne style, ponieważ jest to tylko dowód koncepcji, a nie coś, co chcesz skopiować-wkleić (chyba że naprawdę potrzebujesz bloku do wyświetlania rankingów piłkarskich w takim stylu). I zauważ, że nadal używam SCSS, który kompiluje do CSS przy kompilacji.
Style edytora
/* 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);
}
}
}
Style front-endowe
/* 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;
}
}
}
Dodajemy to do src/style.scss
który dba o stylizację zarówno w edytorze, jak i we frontendzie. Nie będę mógł udostępnić adresu URL demonstracji, ponieważ wymagałoby to dostępu redaktora, ale mam nagrane wideo, aby zobaczyć demo:
Całkiem schludny, prawda? Teraz mamy w pełni funkcjonalny blok, który nie tylko renderuje w interfejsie użytkownika, ale także pobiera dane API i renderuje bezpośrednio w Edytorze bloku — z przyciskiem odświeżania do uruchomienia!
Ale jeśli chcemy wziąć pełny zaletą edytora bloków WordPress, powinniśmy rozważyć zmapowanie niektórych elementów interfejsu użytkownika bloku do kontrolki blokowe do takich rzeczy jak ustawianie koloru, typografii i odstępów. To miły kolejny krok na drodze uczenia się programowania blokowego.