This can be a continuation of my final article about “Rendering Exterior API Knowledge in WordPress Blocks on the Entrance Finish”. In that final one, we realized how one can take an exterior API and combine it with a block that renders the fetched knowledge on the entrance finish of a WordPress web site.
The factor is, we achieved this in a method that stops us from seeing the info within the WordPress Block Editor. In different phrases, we are able to insert the block on a web page however we get no preview of it. We solely get to see the block when it’s printed.
Let’s revisit the instance block plugin we made within the final article. Solely this time, we’re going to utilize the JavaScript and React ecosystem of WordPress to fetch and render that knowledge within the back-end Block Editor as properly.
The place we left off
As we kick this off, right here’s a demo the place we landed within the final article you could reference. You might have seen that I used a render_callback methodology within the final article in order that I could make use of the attributes within the PHP file and render the content material.
Nicely, which may be helpful in conditions the place you might need to make use of some native WordPress or PHP operate to create dynamic blocks. However if you wish to make use of simply the JavaScript and React (JSX, particularly) ecosystem of WordPress to render the static HTML together with the attributes saved within the database, you solely must concentrate on the Edit and Save capabilities of the block plugin.
The Edit operate renders the content material primarily based on what you wish to see within the Block Editor. You may have interactive React parts right here.The Save operate renders the content material primarily based on what you wish to see on the entrance finish. You can’t have the the common React parts or the hooks right here. It’s used to return the static HTML that’s saved into your database together with the attributes.
The Save operate is the place we’re hanging out at this time. We will create interactive parts on the front-end, however for that we have to manually embrace and entry them outdoors the Save operate in a file like we did within the final article.
So, I’m going to cowl the identical floor we did within the final article, however this time you possibly can see the preview within the Block Editor earlier than you publish it to the entrance finish.
The block props
I deliberately not noted any explanations in regards to the edit operate’s props within the final article as a result of that might have taken the main target off of the principle level, the rendering.
In case you are coming from a React background, you’ll doubtless perceive what’s that I’m speaking about, however if you’re new to this, I might suggest trying out parts and props within the React documentation.
If we log the props object to the console, it returns a listing of WordPress capabilities and variables associated to our block:
We solely want the attributes object and the setAttributes operate which I’m going to destructure from the props object in my code. Within the final article, I had modified RapidAPI’s code in order that I can retailer the API knowledge by way of setAttributes(). Props are solely readable, so we’re unable to change them instantly.
Block props are much like state variables and setState in React, however React works on the consumer aspect and setAttributes() is used to retailer the attributes completely within the WordPress database after saving the put up. So, what we have to do is save them to attributes.knowledge after which name that because the preliminary worth for the useState() variable.
The edit operate
I’m going to copy-paste the HTML code that we utilized in football-rankings.php within the final article and edit it a bit to shift to the JavaScript background. Bear in mind how we created two extra information within the final article for the entrance finish styling and scripts? With the way in which we’re approaching issues at this time, there’s no must create these information. As an alternative, we are able to transfer all of it to the Edit operate.
Full code
import { useState } from “@wordpress/aspect”;
export default operate Edit(props) {
const { attributes, setAttributes } = props;
const [apiData, setApiData] = useState(null);
operate fetchData() {
const choices = {
methodology: “GET”,
headers: {
“X-RapidAPI-Key”: “Your Fast 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”,
choices
)
.then((response) => response.json())
.then((response) => {
let newData = { …response }; // Deep clone the response knowledge
setAttributes({ knowledge: newData }); // Retailer the info in WordPress attributes
setApiData(newData); // Modify the state with the brand new knowledge
})
.catch((err) => console.error(err));
}
return (
<div {…useBlockProps()}>
<button onClick={() => getData()}>Fetch knowledge</button>
{apiData && (
<>
<div id=”league-standings”>
<div
className=”header”
fashion={{
backgroundImage: `url(${apiData.response[0].league.brand})`,
}}
>
<div className=”place”>Rank</div>
<div className=”team-logo”>Emblem</div>
<div className=”team-name”>Crew title</div>
<div className=”stats”>
<div className=”games-played”>GP</div>
<div className=”games-won”>GW</div>
<div className=”games-drawn”>GD</div>
<div className=”games-lost”>GL</div>
<div className=”goals-for”>GF</div>
<div className=”goals-against”>GA</div>
<div className=”factors”>Pts</div>
</div>
<div className=”form-history”>Kind historical past</div>
</div>
<div className=”league-table”>
{/* Utilization of [0] is likely to be bizarre however that’s how the API construction is. */}
{apiData.response[0].league.standings[0].map((el) => {
{/* Destructure the required knowledge from all */}
const { performed, win, draw, lose, objectives } = el.all;
return (
<>
<div className=”group”>
<div class=”place”>{el.rank}</div>
<div className=”team-logo”>
<img src={el.group.brand} />
</div>
<div className=”team-name”>{el.group.title}</div>
<div className=”stats”>
<div className=”games-played”>{performed}</div>
<div className=”games-won”>{win}</div>
<div className=”games-drawn”>{draw}</div>
<div className=”games-lost”>{lose}</div>
<div className=”goals-for”>{objectives.for}</div>
<div className=”goals-against”>{objectives.in opposition to}</div>
<div className=”factors”>{el.factors}</div>
</div>
<div className=”form-history”>
{el.type.break up(“”).map((consequence) => {
return (
<div className={`result-${consequence}`}>{consequence}</div>
);
})}
</div>
</div>
</>
);
}
)}
</div>
</div>
</>
)}
</div>
);
}
I’ve included the React hook useState() from @wordpress/aspect relatively than utilizing it from the React library. That’s as a result of if I have been to load the common method, it will obtain React for each block that I’m utilizing. But when I’m utilizing @wordpress/aspect it hundreds from a single supply, i.e., the WordPress layer on high of React.
This time, I’ve additionally not wrapped the code inside useEffect() however inside a operate that is known as solely when clicking on a button in order that we’ve a dwell preview of the fetched knowledge. I’ve used a state variable known as apiData to render the league desk conditionally. So, as soon as the button is clicked and the info is fetched, I’m setting apiData to the brand new knowledge contained in the fetchData() and there’s a rerender with the HTML of the soccer rankings desk obtainable.
You’ll discover that when the put up is saved and the web page is refreshed, the league desk is gone. That’s as a result of we’re utilizing an empty state (null) for apiData‘s preliminary worth. When the put up saves, the attributes are saved to the attributes.knowledge object and we name it because the preliminary worth for the useState() variable like this:
const [apiData, setApiData] = useState(attributes.knowledge);
The save operate
We’re going to do nearly the identical precise factor with the save operate, however modify it a bit bit. For instance, there’s no want for the “Fetch knowledge” button on the entrance finish, and the apiData state variable can be pointless as a result of we’re already checking it within the edit operate. However we do want a random apiData variable that checks for attributes.knowledge to conditionally render the JSX or else it would throw undefined errors and the Block Editor UI will go clean.
Full code
export default operate save(props) {
const { attributes, setAttributes } = props;
let apiData = attributes.knowledge;
return (
<>
{/* Solely render if apiData is accessible */}
{apiData && (
<div {…useBlockProps.save()}>
<div id=”league-standings”>
<div
className=”header”
fashion={{
backgroundImage: `url(${apiData.response[0].league.brand})`,
}}
>
<div className=”place”>Rank</div>
<div className=”team-logo”>Emblem</div>
<div className=”team-name”>Crew title</div>
<div className=”stats”>
<div className=”games-played”>GP</div>
<div className=”games-won”>GW</div>
<div className=”games-drawn”>GD</div>
<div className=”games-lost”>GL</div>
<div className=”goals-for”>GF</div>
<div className=”goals-against”>GA</div>
<div className=”factors”>Pts</div>
</div>
<div className=”form-history”>Kind historical past</div>
</div>
<div className=”league-table”>
{/* Utilization of [0] is likely to be bizarre however that’s how the API construction is. */}
{apiData.response[0].league.standings[0].map((el) => {
const { performed, win, draw, lose, objectives } = el.all;
return (
<>
<div className=”group”>
<div className=”place”>{el.rank}</div>
<div className=”team-logo”>
<img src={el.group.brand} />
</div>
<div className=”team-name”>{el.group.title}</div>
<div className=”stats”>
<div className=”games-played”>{performed}</div>
<div className=”games-won”>{win}</div>
<div className=”games-drawn”>{draw}</div>
<div className=”games-lost”>{lose}</div>
<div className=”goals-for”>{objectives.for}</div>
<div className=”goals-against”>{objectives.in opposition to}</div>
<div className=”factors”>{el.factors}</div>
</div>
<div className=”form-history”>
{el.type.break up(“”).map((consequence) => {
return (
<div className={`result-${consequence}`}>{consequence}</div>
);
})}
</div>
</div>
</>
);
})}
</div>
</div>
</div>
)}
</>
);
}
In case you are modifying the save operate after a block is already current within the Block Editor, it will present an error like this:
That’s as a result of the markup within the saved content material is totally different from the markup in our new save operate. Since we’re in growth mode, it’s simpler to take away the bock from the present web page and re-insert it as a brand new block — that method, the up to date code is used as an alternative and issues are again in sync.
This example of eradicating it and including it once more will be prevented if we had used the render_callback methodology because the output is dynamic and managed by PHP as an alternative of the save operate. So every methodology has it’s personal benefits and drawbacks.
Tom Nowell gives a radical rationalization on what to not do in a save operate in this Stack Overflow reply.
Styling the block within the editor and the entrance finish
Relating to the styling, it’ll be nearly the identical factor we checked out within the final article, however with some minor modifications which I’ve defined within the feedback. I’m merely offering the total kinds right here since that is solely a proof of idea relatively than one thing you wish to copy-paste (except you actually do want a block for exhibiting soccer rankings styled identical to this). And word that I’m nonetheless utilizing SCSS that compiles to CSS on construct.
Editor kinds
/* Goal all of the blocks with the data-title=”Soccer Rankings” */
.block-editor-block-list__layout
.block-editor-block-list__block.wp-block[data-title=”Football Rankings”] {
/* By default, the blocks are constrained inside 650px max-width plus different design particular code */
max-width: unset;
background: linear-gradient(to proper, #8f94fb, #4e54c8);
show: grid;
place-items: middle;
padding: 60px 0;
/* Button CSS – From: https://getcssscan.com/css-buttons-examples – Some properties actually not wanted 🙂 */
button.fetch-data {
align-items: middle;
background-color: #ffffff;
border: 1px stable 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;
colour: rgb(0 0 0 / 0.85);
cursor: pointer;
show: inline-flex;
font-family: system-ui, -apple-system, system-ui, “Helvetica Neue”, Helvetica, Arial, sans-serif;
font-size: 16px;
font-weight: 600;
justify-content: middle;
line-height: 1.25;
margin: 0;
min-height: 3rem;
padding: calc(0.875rem – 1px) calc(1.5rem – 1px);
place: 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;
colour: rgb(0, 0, 0, 0.65);
}
&:hover {
remodel: translateY(-1px);
}
&:energetic {
background-color: #f0f0f1;
border-color: rgb(0 0 0 / 0.15);
box-shadow: rgb(0 0 0 / 0.06) 0 2px 4px;
colour: rgb(0 0 0 / 0.65);
remodel: translateY(0);
}
}
}
Entrance-end kinds
/* Entrance-end block kinds */
.wp-block-post-content .wp-block-football-rankings-league-table {
background: linear-gradient(to proper, #8f94fb, #4e54c8);
max-width: unset;
show: grid;
place-items: middle;
}
#league-standings {
width: 900px;
margin: 60px 0;
max-width: unset;
font-size: 16px;
.header {
show: grid;
hole: 1em;
padding: 10px;
grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
align-items: middle;
colour: white;
font-size: 16px;
font-weight: 600;
background-color: clear;
background-repeat: no-repeat;
background-size: include;
background-position: proper;
.stats {
show: flex;
hole: 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;
.place {
width: 20px;
}
.group {
show: grid;
hole: 1em;
padding: 10px 0;
grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
align-items: middle;
}
.group:not(:last-child) {
border-bottom: 1px stable lightgray;
}
.team-logo img {
width: 30px;
high: 3px;
place: relative;
}
.stats {
show: flex;
hole: 15px;
& > div {
width: 30px;
text-align: middle;
}
}
.last-5-games {
show: flex;
hole: 5px;
& > div {
width: 25px;
peak: 25px;
text-align: middle;
border-radius: 3px;
font-size: 15px;
& .result-W {
background: #347d39;
colour: white;
}
& .result-D {
background: grey;
colour: white;
}
& .result-L {
background: lightcoral;
colour: white;
}
}
}
We add this to src/fashion.scss which takes care of the styling in each the editor and the frontend. I will be unable to share the demo URL since it will require editor entry however I’ve a video recorded so that you can see the demo:
Fairly neat, proper? Now we’ve a completely functioning block that not solely renders on the entrance finish, but additionally fetches API knowledge and renders proper there within the Block Editor — with a refresh button as well!
But when we wish to take full benefit of the WordPress Block Editor, we ought to think about mapping among the block’s UI parts to block controls for issues like setting colour, typography, and spacing. That’s a pleasant subsequent step within the block growth studying journey.
Rendering Exterior API Knowledge in WordPress Blocks on the Again Finish initially printed on CSS-Tips, which is a part of the DigitalOcean household. It is best to get the e-newsletter.
Subscribe to MarketingSolution.
Receive web development discounts & web design tutorials.
Now! Lets GROW Together!