react.dev
Create react project
Run the following command in your terminal: vite dev/guide/
npm create vite@latest
tanstack/react-query
Hooks for fetching, caching and updating asynchronous data in React, Solid, Svelte and Vue
npm i @tanstack/react-query
react-query-devtools
npm i @tanstack/react-query-devtools
Tailwind CSS with Vite
tailwindcss com/docs/guides/vite
Install npm i uuid
my-app\src\App.jsx
//src\App.jsx import { Route, Routes } from "react-router-dom" //npm i react-router-dom import Home from "./pages/Home" import Post from "./pages/Post" function App() { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/post/:slug" element={<Post />} /> </Routes> ) } export default Appmy-app\src\main.jsx
//src\main.jsx import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' import { BrowserRouter } from "react-router-dom"; import App from './App.jsx' // create a client const queryClient = new QueryClient(); createRoot(document.getElementById("root")).render( <StrictMode> <BrowserRouter> <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider> </BrowserRouter> </StrictMode>, );Home Page : my-app\src\pages\Home.jsx
//src\pages\Home.jsx import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query import { getPostList } from "../api/post"; import Pagination from "../components/Pagination"; import { useSearchParams } from "react-router-dom" import PostList from "./PostList"; export default function Home() { const [searchParams] = useSearchParams(); const page = Number(searchParams.get("page")); //console.log(page) const currentPage = Number(page) || 1; //console.log(currentPage) const { isLoading, data, isError, error } = useQuery({ queryKey: ["customers", currentPage], queryFn: () => getPostList(currentPage) }); //console.log(data); if (isLoading) return "Loading..."; if (isError) return `Error: ${error.message}`; const totalPages = Math.ceil(Number(data.totalpage) / Number(data.perpage)); console.log(totalPages); return ( <div className="w-screen py-20 flex justify-center flex-col items-center"> <div className="flex items-center justify-between gap-1 mb-5 pl-10 pr-10"> <h1 className="text-4xl font-bold">Reactjs 18 WordPress Rest API Pagination View Post | Tanstack Query Tailwind CSS</h1> </div> <div className="overflow-x-auto pt-10"> <PostList postlists={data.postlist} /> <div className="flex items-center justify-between my-5"> <Pagination totalPages={totalPages}/> </div> </div> </div> ); }Post List page : my-app\src\pages\PostList.jsx
//my-app\src\pages\PostList.jsx const PostList = ({ postlists }) => { return ( <> <div className="grid md:grid-cols-3 gap-5 mt-10"> {postlists.map((post) => ( <div key={post.id} className="max-w-sm border border-gray-200 rounded-md shadow"> <div className="relative aspect-video"> <img src={post._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url} alt={post.title.rendered} sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" className="rounded-t-md object-cover"/> </div> <div className="p-5"> <h1> <a href={`/post/${post.slug}`}> {post.title.rendered} </a> </h1> </div> </div> ))} </div> </> ); }; export default PostList;View page : my-app\src\pages\Post.jsx
//my-app\src\pages\Post.jsx import { useQuery } from "@tanstack/react-query"; //npm i @tanstack/react-query import { useParams } from "react-router-dom"; import { fetchPost } from "../api/post"; const Read = () => { const { slug } = useParams(); console.log(slug); const { isLoading, isError, data: post, error, } = useQuery({ queryKey: ["posts", slug], queryFn: () => fetchPost(slug), }); console.log(post); if (isLoading) return "loading..."; if (isError) return `Error: ${error.message}`; return ( <div className="min-h-screen flex items-center justify-center bg-slate-100"> <div className="bg-white rounded-sm shadow p-8 text-black"> { post.length ? ( <div> <h1 className="text-2xl font-bold mb-5">{post[0].title.rendered}</h1> <div className="mb-4"> <div dangerouslySetInnerHTML={{ __html: post[0]['content']['rendered'] }} /> </div> </div> ) : ('Loading....') } </div> </div> ) } export default ReadApi : my-app\src\api\post.jsx
//src\api\post.jsx const ITEM_PER_PAGE = 6; export async function getPostList(page) { const response = await fetch(`http://localhost:8888/cairocoders/wp-json/wp/v2/posts?per_page=${ITEM_PER_PAGE}&page=${page}&_embed`); const totalData = response.headers.get('X-WP-Total'); const data = await response.json(); console.log(data); return { postlist: data, totalpage: totalData, perpage: ITEM_PER_PAGE } } export async function fetchPost(slug) { const response = await fetch("http://localhost:8888/cairocoders/wp-json/wp/v2/posts?_embed&slug="+slug); return response.json() }Pagination : my-app\src\components\Pagination.jsx
//src\components\Pagination.jsx import { HiChevronLeft, HiChevronRight } from "react-icons/hi"; //npm install react-icons --save npmjs com/package/react-icons import clsx from "clsx"; //npm i clsx npmjs com/package/clsx import { useSearchParams, useLocation } from "react-router-dom" export const generatePagination = (currentPage, totalPages) => { if (totalPages <= 7) { return Array.from({ length: totalPages }, (_, i) => i + 1); } if (currentPage <= 3) { return [1, 2, 3, "...", totalPages - 1, totalPages]; } if (currentPage >= totalPages - 2) { return [1, 2, 3, "...", totalPages - 2, totalPages - 1, totalPages]; } return [ 1, "...", currentPage - 1, currentPage, currentPage + 1, "...", totalPages, ]; }; const Paginationnumber = ({ totalPages }) => { let location = useLocation(); console.log(location); const { hash, pathname, search } = location; //console.log(pathname); const [searchParams] = useSearchParams(); const currentPage = Number(searchParams.get("page")) || 1; //console.log(currentPage); 2 const allPages = generatePagination(currentPage, totalPages); const createPageURL = (pageNumber) => { const params = searchParams; params.set("page", pageNumber.toString()); return `${pathname}?${params.toString()}`; }; const PaginationNumber = ({ page, href, position, isActive, }) => { const className = clsx( "flex h-10 w-10 items-center justify-center text-sm border", { "rounded-l-sm": position === "first" || position === "single", "rounded-r-sm": position === "last" || position === "single", "z-10 bg-blue-700 border-blue-500 text-white bg-blue-700": isActive, "hover:bg-blue-700": !isActive && position !== "middle", "text-gray-300 pointer-events-none": position === "middle", } ); return isActive && position === "middle" ? ( <div className={className}>{page}</div> ) : ( <a href={href} className={className}> {page} </a> ); }; const PaginationArrow = ({ href, direction, isDisabled }) => { const className = clsx( "flex h-10 w-10 items-center justify-center text-sm border", { "pointer-events-none text-blue-300": isDisabled, "hover:bg-blue-700": !isDisabled, "mr-2": direction === "left", "ml-2": direction === "right", } ); const icon = direction === "left" ? ( <HiChevronLeft size={20} /> ) : ( <HiChevronRight size={20} /> ); return isDisabled ? ( <div className={className}>{icon}</div> ) : ( <a href={href} className={className}> {icon} </a> ); }; return ( <div className="inline-flex"> <PaginationArrow direction="left" href={createPageURL(currentPage - 1)} isDisabled={currentPage <= 1} /> <div className="flex -space-x-px"> {allPages.map((page, index) => { let position; if (index === 0) position = "first"; if (index === allPages.length - 1) position = "last"; if (allPages.length === 1) position = "single"; if (page === "...") position = "middle"; return ( <PaginationNumber key={index} href={createPageURL(page)} page={page} position={position} isActive={currentPage === page} /> ); })} </div> <PaginationArrow direction="right" href={createPageURL(currentPage + 1)} isDisabled={currentPage >= totalPages} /> </div> ); }; export default Paginationnumber;Run
C:\react-js\my-app> npm run dev
themes\cairocoders\functions.php
//themes\cairocoders\functions.php <?php /** * Theme functions and definitions * * @package cairocoders */ add_action('rest_api_init', 'register_rest_images' ); function register_rest_images(){ register_rest_field( array('post'), 'fimg_url', array( 'get_callback' => 'get_rest_featured_image', 'update_callback' => null, 'schema' => null, ) ); } function get_rest_featured_image( $object, $field_name, $request ) { if( $object['featured_media'] ){ $img = wp_get_attachment_image_src( $object['featured_media'], 'app-thumb' ); return $img[0]; } return false; }