Introduction
WordPress is the most popular content management system (CMS) with millions of users. It's a great platform for building a blog, or any other type of website. Of course, it's not a silver-bullet solution, and it also has limitations.
Throughout my career as a web developer, I've developed numerous WordPress plugins with TypeScript, Vue and React. That's one of the reasons why I've decided to write this article to show you the code structure that I use for every plugin.
That's precisely what we set out to do, I'll walk you through the process of creating a custom WordPress plugin, with OOP on the backend and TypeScript on the frontend.
First, we'll install WordPress with Docker. Then we'll create a backend setup for our plugin, and create a communication between our frontend and backend. It's going to be interesting.
It doesn't matter if you already have experience developing plugins for WordPress or if you're just getting started — this guide will be really helpful for you.
Likewise, it's worth mentioning up-front that I love types everywhere in my code. I will not use any static analyzation tools for PHP, just because of simplicity of the article. However, I will still include types everywhere I can in PHP, just because I always do that. It helps PHP Storm and other IDEs to better understand the code and to make it easier to debug in the future.
Foreword
I am not a WordPress expert by any means. Likewise, I have never followed the official WordPress developing guidelines, so I won't use framework/plugin boilerplate here. Instead, we'll start with a code structure that I use for every plugin.
On the frontend, we will use React's JSX syntax. On the backend, we will use Object-Oriented Programming (OOP). Although, OOP is not common in WordPress projects, I still prefer to do it this way because it allows me to create a plugin that is much easier to maintain and extend in the future.
I'm not going to use recommended WordPress code style because I've never used it before, and I don't feel comfortable to use it in PHP. Therefore, I'll do my coding in PSR12 because it's used in modern PHP projects.
WordPress setup
For plugin development, I'm going to use Docker. If you don't know Docker, or you avoid using it for some reason, just skip this section and head over to wordpress.org/download, and install WordPress manually on your computer. However, I recommend using a Docker container for development purposes because installing everything manually is harder.
I will install a fresh WordPress instance on my local machine using a Docker image containing the latest stable version of PHP, MariaDB, and Composer dependency management tool.
Here is the directory structure which I will use for this project:
All the source code is available on GitHub by this link: github.com/SerhiiChoBlog/wordpress-react.
Here is a docker-compose.yml
file:
version: '3.1'
services:
wp:
build:
context: ./wp
container_name: wp
restart: always
ports:
- 8080:80
depends_on:
- db
volumes:
- ./wp/src:/var/www/html
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: user
WORDPRESS_DB_PASSWORD: 111111
WORDPRESS_DB_NAME: wp_react
WORDPRESS_DEBUG: 1
db:
image: mariadb:10.7.3-focal
command: --default-authentication-plugin=mysql_native_password
container_name: db
restart: always
ports:
- 2222:3306
volumes:
- ./db:/var/lib/mysql
environment:
MYSQL_DATABASE: wp_react
MYSQL_USER: user
MYSQL_PASSWORD: 111111
MYSQL_ROOT_PASSWORD: 111111
Dockerfile is pretty simple, we need it just for installing composer into our Docker image.
FROM wordpress:6.0-php8.0-apache
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
Also, I added .dockerignore
file and include /.git
and /db
directories into it. The content of these files you can find on GitHub if you need it.
That's just about it for the Docker setup. This is precisely why developers love Docker because it allows them to build complex environments locally by just running a single command. In our case, the command is: docker-compose up -d
.
Installing WordPress
After the containers are started and running, I visit localhost:8080
URL to view the default WordPress installation page. As you can see on the screen, WordPress is installed and ready. It might take some time depending on your computer.
We follow the next step and choosing the language setting. After clicking on “Continue”, WordPress will show the installation's main page, which requires filling out some basic information.
I click “Install WordPress” button and login into WordPress admin panel. Now my WP admin dashboard is ready for work!
Plugin boilerplate
Before diving into the implementation details, let's create our plugin boilerplate. By boilerplate, I mean the basic structure that I use for every WordPress plugin. I follow this structure most of the time for my plugins, and I use them whenever I need to reuse some functionality.
As I mentioned above, this structure is pretty standard in every plugin that I write, even though it's not perfect. I usually have to make some adjustments to make it work well for each particular plugin, but you can always modify it to meet your specific requirements.
Anyway, I will create a wp-react
directory in wp/src/wp-content/plugins
directory. As you probably know, the plugins
directory is where all the WordPress plugins are located.
My first file in the plugin directory is always called the same way as the plugin directory. For this case, my file is wp-react.php
because the plugin directory is wp-react
. Let's take a look what the file looks like:
<?php
declare(strict_types=1);
/*
Plugin Name: WP React
Author: Serhii Cho
Author URI: https://serhii.io
Description: Test plugin for showing how to make a plugin with React JS and TypeScript
Version: 0.1
License: no
License URI: https://serhii.io
Text Domain: wpreact
Tags: react, typescript, tutorial, test-plugin, psr12
*/
defined('ABSPATH') || exit;
define('WPREACT_PATH', plugin_dir_path(__FILE__));
define('WPREACT_URL', plugin_dir_url(__FILE__));
This comment provides information about the plugin author and plugin version. You can add any additional information you want here. For example, you can use a license for your plugin.
Under the comment, I typically define constants which will be used in the plugin.
ABSPATH
— is defined by WordPress, so you don't need to define it manually. It is defined as an absolute path based on the location of the plugin directory. When it's not defined, it means that you are developing your plugin outside the WordPress installation. Don't bother outputting any error message in this case, just exit.
WPREACT_PATH
— is the absolute path to the plugin directory which I will use in my code (in this case it is pointing to /wp-content/plugins/wp-react
directory).
WPREACT_URL
— is the generated URL for the plugin's directory. This URL will be used to create links to this plugin from the frontend.
Hooks
WordPress allows plugin authors to add custom functionality to certain places in WordPress itself. This can be achieved with hooks. To read about hooks in WordPress, go to the documentation page. I highly recommend reading this page as it will help you avoid making mistakes when writing your plugins.
After creating the first file, I add a Hook.php
file. This file contains all the necessary logic for our plugin. It will be placed in the app
directory.
This is how the file currently looks:
<?php
declare(strict_types=1);
namespace WpReact;
final class Hook
{
public function init(): void
{
foreach (get_class_methods($this) as $method) {
if ($method !== 'init') {
$this->{$method}();
}
}
}
}
It takes all the method names from this class with function get_class_methods
, and executes each one except init
method. What is important here is that it will execute methods in the same order as you write it, from the top to bottom.
Before moving any further, we should do a couple of things. Firstly, we need to add composer autoloading.
cd wp/src/wp-content/plugins/wp-react && composer init
After answering composer questions, I get the composer.json
file with this content:
{
"name": "serhiichornenkyi/wp-react",
"require-dev": {},
"autoload": {
"psr-4": {
"WpReact\\": "app/"
}
},
"authors": [
{
"name": "SerhiiCho",
"email": "serhiicho@protonmail.com"
}
],
"require": {}
}
The namespace “WpReact” will be bound to the app
directory of our plugin. With composer autoloading in place, we can go back to the wp-react.php
file and execute the init
function. To the end of this file, after defining constants, I'll add these two lines:
require_once 'vendor/autoload.php';
(new WpReact\Hook())->init();
Now, every method defined in Hook class will be automatically executed. If you get any errors, try running composer dumpautoload
. It might resolve your problem.
Don't worry if you don't understand why we need a Hook class, it will make sense after we register WordPress shortcodes and assets. Let's do it right after we enable our plugin in WordPress admin panel.
Navigate to “Plugins” menu and enable our WP React plugin there by clicking “Activate” button. Once it's done, we can add our first shortcode.
WordPress shortcodes are PHP functions that let you add some special content to a website pages or posts using a simple syntax, like this: [this_is_my_shortcode]
. You simply insert this shortcode into a page, and it will generate the content you specified in the code.
In our Hook class, I'll add a new private method registerBirdsShortcode
. This shortcode will generate a list of birds on our frontend page.
private function registerBirdsShortcode(): void
{
add_shortcode('wpreact_birds', static function (): string {
add_action('wp_enqueue_scripts', function () {
$path = WPREACT_PATH . 'assets/birds.js';
$url = WPREACT_URL . 'assets/birds.js';
wp_register_script('wpreact-birds-js', $url, [], $path);
wp_localize_script('wpreact-birds-js', 'wpReactBirdsGlobals', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wpreact-birds'),
]);
wp_enqueue_script('wpreact-birds-js');
});
return 'Birds are cute 🕊';
});
}
There is a lot of action going on, but it's pretty straightforward. First we register a shortcode [wpreact_birds]
and passing a callback function that is going to return a content of the shortcode. In our case, our shortcode is simply a string: Birds are cute 🕊.
The add_action
function allows you to execute a passed callback function at specific points during WordPress execution. In our case, the action name is wp_enqueue_scripts
, from the WordPress documentation we can read that it will execute a passed callback when scripts and styles are enqueued.
Note. We can call add_action
function anywhere inside our WordPress plugin, but in our case we would like to register JavaScript only when shortcode is loaded into the page. We aim to avoid loading JS on all the pages on the site for better performance.
Another thing I want you to notice, is that a wp_localize_script
function registers a global JavaScript object wpReactBirdsGlobals
with 2 properties, ajaxUrl and nonce. If you ever need to pass some public value from backend to frontend, you can do it with this array.
To check if our code works, let's create an assets/birds.js
file in our plugin root with console.log('Works!')
inside the file. Let's see if that message is displayed on the console when we load the page with shortcode: [wpreact_first]
. For that, we need to create a new page and insert a shortcode in it.
In WordPress admin panel, navigate to the Pages section and click Add New. Give your new page a title and a shortcode [wpreact_birds]
.
Your interface may differ from mine because you might use a different WordPress version or have a different editor installed. Click Publish button and navigate to a newly created page. Open the console, you should see a message: “Works!”, and the page content: “This is the first shortcode”. That means our code works as expected.
Since our JavaScript loads, we can now install React and compile it into the plain JavaScript, which will be generated in a birds.js
file by our compiler. Let's do it right now and install all the required dependencies.
Install React
There are few ways to compile React to a plain JavaScript, since this article is about my personal workflow I prefer using a wrapper around the Webpack called: Laravel Mix. It is easy to install and even easier to configure.
I'm going to run this command to create a package.json
file and install Laravel Mix and everything else that we need. First, we'll enter the plugin directory via terminal command cd wp/src/wp-content/plugins/wp-react
and install all we need by running this command:
yarn init -y \
&& yarn add laravel-mix @types/react @types/react-dom --dev \
&& yarn add react react-dom axios
Great! To configure Laravel Mix, we need to create a webpack.mix.js
file in the root directory and configure Webpack. Here is how I set up the configuration file:
const mix = require('laravel-mix')
mix.ts('resources/ts/birds/main.tsx', 'assets/birds.js')
.react()
.sourceMaps()
.disableNotifications()
You can see why I prefer using Laravel Mix over other options. It allows us to use all the power of Webpack with nice configurations and great community support. I love how readable it is and how easy it is to remember. Let's do the last step and create tsconfig.json file by running tsc --init
.
If you don't have TypeScript installed on your machine, create tsconfig.json
manually with this JSON:
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"jsx": "react",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
The only line which I added manually is "jsx": "react"
, everything else is generated by running the tsc --init
.
I will also add 2 scripts to a package.json
file to run Laravel Mix commands. This is how my package.json
file looks like:
{
"name": "wp-react",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"laravel-mix": "^6.0.49"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"scripts": {
"watch": "mix watch",
"prod": "mix --production"
}
}
Now, we have everything in place to compile React JSX files to plain JavaScript and save them into the assets directory of our plugin. I can now run yarn watch
or npm run watch
to ask Webpack to watch changes in our resources/ts/birds
directory.
Let's modify the main.tsx
file to React instantiation and see if it compiles.
import React from "react"
import { render } from "react-dom"
import { createRoot } from 'react-dom/client'
window.addEventListener('load', () => {
const target = document.getElementById('wpreact-birds')
if (!target) {
throw new Error('Cannot find element #wpreact-birds')
}
const root = createRoot(target)
root.render(<h2>Birds are cute creatures</h2>)
})
We can clearly see that when the page is loaded, we create a React instance, and render JSX with h2 tag into a #wpreact-birds
element. Since we don't have an element with this id attribute, let's make it so our shortcode generates it.
So, in our registerBirdsShortcode()
method, I'll change the return statement to this:
return '<div id="wpreact-birds"></div>';
Run yarn watch
if you haven't already ran this command and check the Birds page to see if our shortcode changes to a React app. In my case, the Birds page looks like this:
Tip. If you don't get this result, make sure your yarn watch
command is running. Occasionally, you need to run it twice because it installs additional dependencies.
Congratulations! You have successfully created a WordPress plugin with TypeScript and React. Of course, we are nowhere near done with our plugin yet, but the base is already there, and we can focus on getting data from the server and displaying it on the frontend.
Fetch birds from the server
Before we do anything else, let's create an App.tsx
file in our resources/ts/birds
directory, which will be our main parent component.
import React from 'react'
const App: React.FC = () => {
return (
<h2>Birds are cute creatures</h2>
)
}
export default App
Now, we can go back to the main.tsx
, and import App.tsx
there with strict mode enabled.
import React from "react"
import { render } from "react-dom"
import { createRoot } from 'react-dom/client'
import App from './App'
window.addEventListener('load', () => {
const target = document.getElementById('wpreact-birds')
if (!target) {
throw new Error('Cannot find element #wpreact-birds')
}
const root = createRoot(target)
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
)
})
That's mostly how you would do in a normal React app. Now, we can finally create the Birds.tsx component and make it fetch data from the server. I'm going to create a components/Birds/Birds.tsx
file.
For now, before we start fetching data from the server, let's create the frontend first. Here is the Birds.tsx
file with some temporary data:
import React from 'react'
import './Birds.css'
const Birds: React.FC = () => {
return (
<ul className="Birds-list">
<li className="Birds-item">
<img
src="https://i.imgur.com/GJKdVfL.jpeg"
alt="Bird looking right"
/>
<h2>Bird looking right</h2>
</li>
<li className="Birds-item">
<img
src="https://i.imgur.com/1lC07Hh.jpeg"
alt="Cute sparrow bird"
/>
<h2>Cute sparrow bird</h2>
</li>
</ul>
)
}
export default Birds
There is also a Birds.css
file imported at the top. I'm not going to include any CSS in this article, but here is the link to the Birds.css
file on GitHub if you need it. To see the result in the browser, I will also import the Birds component in the App.tsx
file like this:
import React from 'react'
import Birds from './components/Birds/Birds'
const App: React.FC = () => {
return (
<Birds />
)
}
export default App
On our frontend page, we can see the result:
Our component is properly displayed without errors. If you get any, try to follow all the steps and see what you did wrong. Let's move on and create a backend which will give us the array of birds. We'll use this data to render it on the frontend.
Backend
All we need from the backend functionality is to get data which will be used on our frontend. I'm going to start from creating the app/Http/Ajax/AjaxEntry.php
file. As the name of the file suggests, this class will receive the request from the frontend and point this request to a certain handler. A handler is a class, that will process the request and return the response back to the frontend.
This is how the AjaxEntry.php file looks like:
<?php
declare(strict_types=1);
namespace WpReact\Http\Ajax;
use JsonException;
use Throwable;
use WpReact\Http\Ajax\Handlers\GetBirdsHandler;
use WpReact\Http\Ajax\Handlers\Handler;
use WpReact\Http\Ajax\Requests\GetBirdsRequest;
final class AjaxEntry
{
/**
* Determine what method need to call when AJAX request comes.
* Each action name points to a handler, which will be executed
* when the request will be received.
*
* @return array<string, callable(): Handler>
*/
public function mapActionsToHandlers(): array
{
return [];
}
/**
* @throws JsonException
*/
public function openEntry(): void
{
foreach ($this->mapActionsToHandlers() as $action_name => $callback) {
$action = function () use ($callback): void {
check_ajax_referer('wpreact-birds');
try {
echo $callback()->handle();
} catch (Throwable $e) {
echo Response::jsonError($e->getMessage());
}
wp_die();
};
add_action("wp_ajax_$action_name", $action);
add_action("wp_ajax_nopriv_$action_name", $action);
}
}
}
The mapActionsToHandlers
method just returns the array of callbacks, each callback handles the particular request. In other words, every request will have a unique action name, in mapActionsToHandlers
method we map each action name with a callback function, that will be executed after the request is received.
The openEntry
method just assigns each callback function to the wp_ajax_*
and wp_ajax_nopriv_*
actions. It's just the way of registering the AJAX endpoint in WordPress. Every POST request to this endpoint will be automatically processed by the corresponding callback. Adding wp_die()
after AJAX callback will terminate the Ajax request so that we don't have to wait for the full response.
Of course, there are many ways of handling AJAX requests on the backend of your WordPress plugin. This is just one way that I, personally, like to implement it in my projects. I hope you find the above explanation useful. Let's create an AJAX handler so that you have a better understanding about how it works behind the scenes.
I will create an app/Http/Ajax/Handlers/GetBirdsHandler.php
file. This class is responsible for processing the request and giving back the JSON data. Creating this handler also involves a couple of steps. Firstly, all AJAX handlers needs to have a common interface app/Http/Ajax/Handlers/Handler.php
. This interface is just a simple way to make sure that all the handlers have a handle method which returns string. It acts as a wrapper for the actual function which will be executed when the request is received.
<?php
declare(strict_types=1);
namespace WpReact\Http\Ajax\Handlers;
interface Handler
{
public function handle(): string;
}
Pretty simple, right? Now we can create a GetBirdsHandler
class which will implement the Handler interface.
<?php
declare(strict_types=1);
namespace WpReact\Http\Ajax\Handlers;
use WpReact\Http\Ajax\Requests\GetBirdsRequest;
use WpReact\Http\Ajax\Response;
class GetBirdsHandler implements Handler
{
public function __construct(private GetBirdsRequest $request)
{
}
public function handle(): string
{
return '';
}
}
You can see that we also have a GetBirdsRequest
injected in the constructor. This is just a wrapper around our $_POST variable. This is what it looks like:
<?php
declare(strict_types=1);
namespace WpReact\Http\Ajax\Requests;
class GetBirdsRequest extends Request
{
private const DEFAULT_BIRDS_LIMIT = 2;
public function getBirdsLimit(): int
{
$limit = $this->data['birds_limit'] ?? self::DEFAULT_BIRDS_LIMIT;
return (int) $limit;
}
}
I use these request wrappers for each handler that needs a request. You see that I even give similar names to a handler and request so that it's easier to understand which request relates to each handler. In this case, a get_birds
action corresponds to GetBirdsHandler
which has an injected GetBirdsRequest
class.
Tip. For some people, this whole logic seems a bit difficult to grasp. Why even make request wrappers and pass them into the handler when we can just use a global $_POST variable? Well, the problem is that you don't know what hides inside the $_POST variable. It means that you don't get any code completion or type errors when using request values. Moreover, request wrappers can hide some extra logic which can help you to clean up your code. All this makes sense after some time when your plugin grows bigger.
The Request class which is being extended by all our requests is pretty simple. This is what it looks like:
<?php
declare(strict_types=1);
namespace WpReact\Http\Ajax\Requests;
abstract class Request
{
/**
* @var array<string>
*/
protected array $data;
public function __construct()
{
$this->data = $_POST;
}
}
The $data
property is an array of strings which contains all the POST data received by the server. That's it, nothing fancy going on here.
For our simple example, we have too much logic here. However, let's say that we have a few handlers that need different request objects. All we have to do is, create a new request objects and pass it into the handler's constructor. The big advantage, is that we encapsulate request data into their objects. I do it in all WordPress plugins and I always fascinated how helpful it is.
Let's make so that this handler gives back the list of birds. For this, we already have a GetBirdsHandler
, let's finish the handle method.
public function handle(): string
{
$birds = [
[
'url' => 'https://i.imgur.com/GJKdVfL.jpeg',
'title' => 'Bird looking right',
],
[
'url' => 'https://i.imgur.com/1lC07Hh.jpeg',
'title' => 'Cute sparrow bird',
],
];
$birds = array_splice($birds, 0, $this->request->getBirdsLimit());
header('Content-Type: application/json');
return json_encode($birds, JSON_THROW_ON_ERROR);
}
Before moving any further, I want to refactor this method because the last 2 lines are always going to be present when handler gives a JSON response. To prevent repeating myself in the future, I'll create a reusable method which will contain these last 2 lines from handle method.
I'll create a Response class in an app/Http/Ajax
directory with the method json
and jsonError
. Here is the code:
<?php
declare(strict_types=1);
namespace WpReact\Http\Ajax;
use JsonException;
class Response
{
/**
* @throws JsonException
*/
public static function json(array|object $data): string
{
header('Content-Type: application/json');
return json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
}
/**
* @throws JsonException
*/
public static function jsonError(string $message): string
{
return self::json(['error' => WP_DEBUG ? $message : 'Server error']);
}
}
The jsonError
method is just a syntactic sugar to simplify a readability of the openEntry
method. As for handler, we can now remove those last 2 lines from the handle method and return Response::json
from it:
$birds = array_splice($birds, 0, $this->request->getBirdsLimit());
return Response::json($birds);
Our handle method looks much simpler now. We can now register our handler in the mapActionsToHandlers
method like this:
public function mapActionsToHandlers(): array
{
return [
'get_birds' => function (): Handler {
return new GetBirdsHandler(new GetBirdsRequest());
},
];
}
The “get_birds” key here is the name of the action, the callback is something that will be executed when a POST request comes in with the parameter action: “get_birds”
. This way, we know which callback to execute.
Let's do the last step for the backend part and move to the front end part of the plugin. In our final step for the backend part, we'll execute an openEntry
method when WordPress initializes our plugin.
That's why we have the Hook class in place. I'll add a registerApi
method to it right after the registerBirdsShortcode
:
/**
* @throws JsonException
*/
private function registerApi(): void
{
$ajax_entry = new \WpReact\Http\Ajax\AjaxEntry();
$ajax_entry->openEntry();
}
The registerApi
method will be executed automatically by WordPress when the init
method is called in our wp-react.php
file. Now, our backend is ready to receive the requests from React.
AJAX requests
In almost all of my WordPress plugins, I always have the same JavaScript file that I copy and paste from one project to another with small changes. This file is called “Request.ts”. It basically contains the core method that I use for all my HTTP requests. This is what it looks like for this particular project:
import axios from 'axios'
export type RequestData = {
method: string
params?: string | { name: string, value: string | Blob }[]
}
export type RequestInterface = {
send: () => Promise<any>
}
class Request<T> implements RequestInterface {
private readonly data: RequestData
public constructor(data: RequestData) {
this.data = data
}
public send(): Promise<{ data: T }> {
return axios.post(window.wpReactBirdsGlobals.ajaxUrl, this.createParams())
}
private createParams(): FormData {
const params = new FormData()
params.append('action', this.data.method)
params.append('_ajax_nonce', window.wpReactBirdsGlobals.nonce)
if (this.data.params) {
if (typeof this.data.params === 'string') {
params.append('data', this.data.params)
} else {
this.data.params.forEach(param => {
params.append(param.name, param.value)
})
}
}
return params
}
}
export default Request
If you value your time like myself, you understand the concept of abstraction. Abstraction is, roughly speaking, the process of bundling complex or repetitive tasks into a simplified form that is easy to reuse and simplify later on. This is undoubtedly what Request.ts file does for us. It abstracts things that I would otherwise repeat each time when I make an AJAX request.
I will put the Request.ts
file into a resources/ts/modules
directory and use it later on for a POST request.
In the Request class, we use the window.wpReactBirdsGlobals
variable to access our plugin's specific data. You may have noticed that the name of this variable matches to what we have defined in the Hook class when we called the wp_localize_script
function. That's precisely what the wpReactBirdsGlobals
variable will contain.
To tell TypeScript about our global variable, I'm going to create a globals.d.ts
file in the resources/ts
directory with this content:
interface Window {
wpReactBirdsGlobals: {
ajaxUrl: string
nonce: string
}
}
Now, TypeScript knows that the window
object has the wpReactBirdsGlobals
property with some data.
You can explore the Request class to understand how it works under the hood, but I will continue with the article and write the function which fetches the list of birds from the server. For this, I'll open the Birds.tsx
file and import this Request
module from a resources/ts/modules
directory.
This is what the final Birds component looks like:
import React, { useState, useEffect } from 'react'
import './Birds.css'
import Request from '../../../modules/Request'
type Bird = {
url: string
title: string
}
const Birds: React.FC = () => {
const [birds, setBirds] = useState<Bird[]>([])
useEffect(() => fetchBirds(), [])
function fetchBirds(): void {
const params = {
method: 'get_birds',
params: [
{ name: 'birds_limit', value: '1' },
],
}
new Request<Bird[]>(params)
.send()
.then(resp => setBirds(resp.data))
.catch(err => console.error(err))
}
return (
<ul className="Birds-list">
{birds.map(bird => {
return (
<li key={bird.title} className="Birds-item">
<img src={bird.url} alt={bird.title} />
<h2>{bird.title}</h2>
</li>
)
})}
</ul>
)
}
export default Birds
When we test it in the browser, we can see that it works just fine:
We see only one bird, that's because we pass a birds_limit
parameter with value 1. When we change it to 2, the two birds will show up.
Conclusion
In this article, I showed you my workflow when it comes to developing a WordPress plugin with React and TypeScript. You can extend the code as it is written here to create a more complex plugin.
Of course, it's not everything that I wanted to show you, if you're interested in learning more about web development, you can subscribe for new posts. If you want more WordPress tutorials or any other tutorials, you can let me know on social media, and I'll write them as soon as I get the chance.
Keywords: ts, docker, oop, psr12, ajax, frontend, backend, fullstack